Пример использования предиката для замены «если» в c #?

2

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

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

  • 1
    Похоже, связано с этим вопросом: stackoverflow.com/questions/1554180/… (не дурак, просто контекст)
  • 0
    Это также может помочь - stackoverflow.com/questions/556425/predicate-delegates-in-c
Показать ещё 2 комментария
Теги:
predicate

5 ответов

9
Лучший ответ

Независимо от того, что они говорят, если это не зло. Могут быть конкретные случаи, для которых предикат является лучшим выбором, чем 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

Они просто разные. Предикат более сложный, чем простой оператор 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);
  • 0
    Хороший ответ. Не знал немного о назначении делегатов в предикаты.
  • 0
    У вас все еще есть «зло, если» в первом примере :)
Показать ещё 2 комментария
2

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

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

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

0
    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

Я не использую их для прямого "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".

Ещё вопросы

Сообщество Overcoder
Наверх
Меню