Пример использования предиката для замены «если» в c #? - PullRequest
2 голосов
/ 13 октября 2009

Я читал, что ключевое слово 'if' - зло, и лучше использовать предикат для замены if. Потом я погуглил, но все равно не понял.

Может ли кто-нибудь быть добрым, чтобы привести пример?

Ответы [ 5 ]

9 голосов
/ 13 октября 2009

Неважно, что они говорят, если не зло. В некоторых случаях Predicate является лучшим выбором, чем 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));
2 голосов
/ 13 октября 2009

Например, когда у вас есть такой цикл:

List<Employee> retiredEmployees = new List<Employee>();
foreach (Employee employee in EmployeeList)
{
    if (employee.IsRetired)
        retiredEmployees.Add(employee);
}

Используя предикат, вы должны изменить его на:

retiredEmployees = EmployeeList.FindAll(e => e.IsRetired);

Но я считаю, что в целом "если утверждение считается злом" дискуссия, predicate против if только что упоминается как частный случай использования ООП и функционального программирования по сравнению с процедурным программированием. Эту парадигму можно легко обобщить для любого типа делегата (не только предиката) или любого использования ООП для замены условного:

Например, если вы просматриваете свой код, вы можете легко найти такой код:

public class Employee
{
    private bool _isRetired;
    private double _amount;
    public double GetPayAmount()
    {
         if (_isRetired)
            return _amount * 0.9;
         else
            return _amount;
    }
}

Сторонники чистого ООП скажут вам, что вам необходимо немедленно извлечь другого типа сотрудника и обрабатывать каждую ветвь как отдельный подтип, что удалит «утверждение зла, если» :

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;
   }
}

Хотя код легче поддерживать таким образом, объем кода во втором случае явно удвоился. Для такой простой иерархии нет необходимости выполнять рефакторинг на этом этапе. Если вы решите, что вам нужно еще много особых случаев, то ваше условие может стать слишком сложным, и вы сможете легко изменить его позже.

2 голосов
/ 13 октября 2009

Они просто разные. Предикат является более сложным, чем простое выражение 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)) на самом деле оценивает в enumerator. Текущий определяется во время реализации предиката. Код не знает, какой тип 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);
0 голосов
/ 21 декабря 2012
    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"); }
0 голосов
/ 13 октября 2009

Я не использую их для прямых конструкций "если ... еще", кроме как внутри поиска, поскольку это также устраняет необходимость в конструкциях цикла. Например

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".

...