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

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

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


person Benny    schedule 13.10.2009    source источник
comment
Похоже, это связано с этим вопросом: stackoverflow.com/questions/1554180/ (не обман, просто контекст)   -  person Greg Hewgill    schedule 13.10.2009
comment
Это также может помочь - stackoverflow.com/questions/556425/predicate- делегаты-в-ц   -  person Anand Shah    schedule 13.10.2009
comment
Программа без ветвления действительно очень жаль.   -  person Cecil Has a Name    schedule 13.10.2009
comment
Программа без ветвления - это прекрасно. ;-)   -  person Nick Hodges    schedule 09.02.2013


Ответы (4)


Что бы ни говорили, если не зло. Могут быть конкретные случаи, для которых предикат является лучшим выбором, чем 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));
person Vinko Vrsalovic    schedule 13.10.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)) фактически оценивает для перечислителя. 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);
person CRice    schedule 13.10.2009
comment
Хороший ответ. Не знал о назначении делегатов предикатам. - person Bartek Tatkowski; 13.10.2009
comment
У тебя все равно зло, если в первом примере :) - person Groo; 13.10.2009
comment
У меня нет проблем с "если", оно служит цели - person CRice; 14.10.2009
comment
@Groo - Как вы предлагаете написать мой метод поиска без использования if? - person CRice; 14.10.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 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;
   }
}

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

person Groo    schedule 13.10.2009

Я сам не использую их для прямых конструкций 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».

person ChrisBD    schedule 13.10.2009