Я читал, что ключевое слово if - зло, и лучше использовать предикат вместо if. Затем я погуглил, но все равно не понял.
Может ли кто-нибудь привести пример?
Я читал, что ключевое слово if - зло, и лучше использовать предикат вместо if. Затем я погуглил, но все равно не понял.
Может ли кто-нибудь привести пример?
Что бы ни говорили, если не зло. Могут быть конкретные случаи, для которых предикат является лучшим выбором, чем if (или набор if).
Например,
foreach (Foo f in fooList) {
if (f.Equals(fooTarget)) {
return f;
}
}
по сравнению с (.NET 2.0)
fooList.Find(delegate (Foo f) { return f.Equals(fooTarget); });
или позже)
fooList.Find(f => f.Equals(fooTarget));
Они просто разные. Предикат сложнее простого оператора if. Предикат - это в основном указатель на метод (делегат), который привязан к типу, который он принимает в качестве параметра и возвращает истину / ложь.
Представьте, что вы используете универсальные шаблоны и, подобно методу find в общих списках, как он может узнать, какие типы находятся в списке, до его инициализации. Таким образом, метод find просто использует предикат и не знает, как этот предикат будет реализован.
public T Find(Predicate<T> p)
{
//iterate through the collection and return the first match
IEnumerator<T> enumerator = this.GetEnumerator();
while (enumerator.MoveNext())
{
if (p(enumerator.Current))
{
return enumerator.Current;
}
}
return default(T);
}
В этом случае используется предикат, но то, что (p (enumerator.Current)) фактически оценивает для перечислителя. Current определяется во время реализации предиката. Код не знает, какой тип T здесь окажется.
Вот несколько способов присвоить предикат методу
Predicate<string> findShortNames1 = x => x.Length == 3; // lambda expression
Predicate<string> findShortNames2 = delegate(string x) { return x.Length == 3; }; // anonymous method
Predicate<string> findShortNames3 = MatchOnShortNames; //existing method
// ...
private bool MatchOnShortNames(string s)
{
return s.Length == 3;
}
Тогда использование похоже на
someList.FindAll(findShortNames1);
Например, всякий раз, когда у вас есть такой цикл:
List<Employee> retiredEmployees = new List<Employee>();
foreach (Employee employee in EmployeeList)
{
if (employee.IsRetired)
retiredEmployees.Add(employee);
}
Используя предикат, вам нужно будет изменить его на:
retiredEmployees = EmployeeList.FindAll(e => e.IsRetired);
Но я считаю, что во всех дебатах "если утверждение считается злом" , predicate
vs if
просто упоминается как частный случай использования ООП и функционального программирования против процедурного программирования. Эту парадигму можно легко обобщить на любой тип делегата (не только на предикат) или любое использование ООП для замены условного:
Например, если вы просматриваете свой код, вы легко можете найти такой код:
public class Employee
{
private bool _isRetired;
private double _amount;
public double GetPayAmount()
{
if (_isRetired)
return _amount * 0.9;
else
return _amount;
}
}
Сторонники чистого ООП скажут вам, что вам немедленно нужно выделить другой тип сотрудников и обрабатывать каждую ветвь как другой подтип, что удалит «злой оператор if»:
public interface IEmployee
{
double GetPayAmount();
}
public class Employee : IEmployee
{
private double _amount;
public double GetPayAmount()
{
return _amount;
}
}
public class RetiredEmployee : IEmployee
{
private double _amount;
public double GetPayAmount()
{
return _amount * 0.9;
}
}
Хотя таким образом код легче поддерживать, объем кода во втором случае явно удвоился. Для такой простой иерархии на этом этапе нет необходимости в рефакторинге. Если вы решите, что вам нужно гораздо больше особых случаев, тогда ваше условное выражение может стать слишком сложным, и вы легко сможете его реорганизовать позже.
Я сам не использую их для прямых конструкций if ... else, кроме поиска внутри, поскольку это также устраняет необходимость в конструкциях цикла. Например
int index = this.listObjects.FindIndex(x => x.PropertyA == objectItem.PropertyA);
or
List<ClassA> listClass = new List<ClassA>();
//... some more code filling listClass with ClassA types ...
ClassA tempClassA = listClass.FirstOrDefault().Where(x=> x.PropertyA == someValue);
Я должен признать, что если есть простое сравнение для одного элемента, то я использую конструкцию «if ... else».