Я читал, что ключевое слово if есть зло, и лучше использовать предикат для замены if. Тогда я googled, но все равно не понимаю.
Может ли кто-нибудь быть добрым, чтобы представить пример?
Независимо от того, что они говорят, если это не зло. Могут быть конкретные случаи, для которых предикат является лучшим выбором, чем if (или набор ifs).
Например,
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. Предикат - это в основном указатель на метод (делегат), который привязан к типу, который он принимает в качестве параметра, и возвращает true/false.
Представьте, что вы используете generics и как метод 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)) фактически оценивает об enumerator.Current определяется во время реализации предиката. Код не знает, какой тип T wil окажется здесь.
Вот несколько способов назначить предикат методу
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;
}
}
Чистые сторонники ООП скажут вам, что вам сразу нужно извлечь другой тип сотрудника и обработать каждую ветвь как другой подтип, который удалит выражение "evil 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;
}
}
Хотя код проще поддерживать таким образом, количество кода во втором случае явно удваивается. Для простой иерархии, как это, мало нужно делать рефакторинг на этом этапе. Если вы решите, что вам нужно еще много особых случаев, то ваше условное может стать слишком сложным, и вы можете легко его реорганизовать позже.
static void Main()
{
string[] names = { "Lukasz", "Darek", "Milosz" };
foreach (var item in names)
{
if (ContainsL(item))
Console.WriteLine(item);
}
string match1 = Array.Find(names, delegate(string name) { return name.Contains("L"); });
//or
string match2 = Array.Find(names, delegate(string name) { return name.Contains("L"); });
//or
string match3 = Array.Find(names, x => x.Contains("L"));
Console.WriteLine(match1 + " " + match2 + " " + match3); // Lukasz Lukasz Lukasz
}
static bool ContainsL(string name) { return name.Contains("L"); }
Я не использую их для прямого "if... else", строит себя, кроме inn-запросов, поскольку он также устраняет необходимость в конструкциях цикла. Например
int index = this.listObjects.FindIndex(x => x.PropertyA == objectItem.PropertyA);
или
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".