2

我读到'if'关键字是邪恶的,最好使用谓词来替换if。然后我google了一下,还是不明白。

任何人都可以提供一个例子吗?

4

5 回答 5

9

不管他们说什么,如果不是邪恶的。在某些特定情况下,Predicate 是比 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));
于 2009-10-13T06:22:59.497 回答
2

他们只是不同。谓词比简单的 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.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);
于 2009-10-13T06:49:24.983 回答
2

例如,每当您有这样的循环时:

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

使用谓词,您必须将其更改为:

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

但我相信,在整个“如果语句被认为是邪恶的”辩论中,predicatevsif只是作为使用 OOP 和函数式编程与过程式编程的特例而被提及。这种范式可以很容易地推广到任何委托类型(不仅仅是谓词),或者任何使用 OOP 来替换条件:

例如,如果您浏览您的代码,您可能很容易找到如下代码:

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

纯 OOP 支持者会告诉您,您立即需要提取不同类型的员工并将每个分支作为不同的子类型处理,这将删除“邪恶的 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;
   }
}

虽然这样代码更容易维护,但第二种情况下的代码量明显翻了一番。对于这样一个简单的层次结构,在这个阶段几乎不需要进行重构。如果您决定需要更多特殊情况,那么您的条件可能会变得过于复杂,您可以在以后轻松地对其进行重构。

于 2009-10-13T07:17:49.437 回答
0

除了内部搜索之外,我自己不会将它们用于直接的“if ... else”构造,因为它还消除了对循环构造的需要。例如

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”构造。

于 2009-10-13T06:23:14.240 回答
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"); }
于 2012-12-20T21:46:54.630 回答