【问题标题】:An example for using predicate to replace 'if' in c#?在 C# 中使用谓词替换 'if' 的示例?
【发布时间】:2009-10-13 06:13:13
【问题描述】:

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

谁能提供一个例子?

【问题讨论】:

标签: c# predicate


【解决方案1】:

不管他们说什么,如果不是邪恶的。在某些特定情况下,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));

【讨论】:

    【解决方案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);
    

    【讨论】:

    • 不错的答案。不知道如何将代表分配给谓词。
    • 在第一个例子中你仍然有一个“邪恶的 if” :)
    • 我对 'if' 没有意见,它是有目的的
    • @Groo - 你如何建议我的 find 方法应该在不使用 'if' 的情况下编写?
    【解决方案3】:

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

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

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

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

    但我相信,在整个 "if statement considered evil" 辩论中,predicateif 只是作为使用 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;
       }
    }
    

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

    【讨论】:

      【解决方案4】:

      我自己不将它们用于直接的“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);
      

      我必须承认,如果只是对一个项目进行直接比较,那么我会使用 and "if ... else" 构造。

      【讨论】:

        【解决方案5】:
            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"); }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-05-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多