【问题标题】:Optional delegates in C# [duplicate]C#中的可选委托[重复]
【发布时间】:2011-09-24 03:21:11
【问题描述】:

这是两个扩展方法重载的简单示例

public static class Extended 
{
    public static IEnumerable<int> Even(this List<int> numbers)
    {
        return numbers.Where(num=> num % 2 == 0);
    }

    public static IEnumerable<int> Even(this List<int> numbers, Predicate<int> predicate)
    {
        return numbers.Where(num=> num % 2 == 0 && predicate(num));
    }
}

我希望能够通过将委托设置为可选来将它们合并为一个:

public static class Extended 
{
    public static IEnumerable<int> Even(this List<int> numbers, Predicate<in> predicate = alwaysTrue)
    {
        return numbers.Where(num=> num % 2 == 0 && predicate(num));
    }

    public static bool alwaysTrue(int a) { return true; }
}

但是,编译器会抛出错误:

'predicate' 的默认参数值必须是编译时常量

我看不出我的 alwaysTrue 函数如何不是常量,但是,嘿,编译器知道得更好 :)

有没有办法让委托参数可选?

【问题讨论】:

    标签: c# c#-4.0 delegates optional-parameters


    【解决方案1】:

    它不是常量,因为您从方法组创建了一个委托……就 C# 语言而言,它不是编译时常量。

    如果您不介意稍微滥用null 的含义,您可以使用:

    private static readonly Predicate<int> AlwaysTrue = ignored => true;
    
    public static List<int> Even(this List<int> numbers,
                                 Predicate<int> predicate = null)
    {
        predicate = predicate ?? AlwaysTrue;
        return numbers.Where(num=> num % 2 == 0 && predicate(num));
    }
    

    (您可以仍然将AlwaysTrue 设为方法并使用方法组转换,但上述方法稍微通过只创建一次委托实例来提高效率.)

    【讨论】:

    • 有时我希望在 SO 上有多个接受。所有答案都是正确的,但是 Jon 的答案似乎对我来说最有利可图,因为他让我去阅读有关方法组的内容,以实际学习一些我不知道的东西。
    • 你为什么说AlwaysTrue 作为匿名方法比命​​名方法更有效?我不了解您的 方法组转换 部分。即使AlwaysTrue 是方法,predicate = predicate ?? AlwaysTrue; 也可以编写,并且在predicate 为空的情况下,调用方法。即使这样,委托也只创建一次,对吗?所以现在它是关于命名方法与匿名方法的。后者的性能更高吗?
    • @nawfal:使用字段意味着当您调用Even 时,它不会创建新的委托实例,因为它会重用该字段中的委托实例。使用方法组转换,假设 predicate 为 null,这将在每次调用时创建一个新的委托实例 - 委托不会被缓存。
    【解决方案2】:
    public static List<int> Even(this List<int> numbers, Predicate<in> predicate = null)
    {
        return numbers.Where(num=> num % 2 == 0 && (predicate == null || predicate(num)));
    }
    

    【讨论】:

      【解决方案3】:

      您要做的就是让它成为null,然后将其视为始终为真

      您有两种选择,将代码加倍以省略委托调用,这将在您不通过委托的情况下执行得更快。

      public static List<int> Even(this List<int> numbers, Predicate<int> predicate = null)
      {
          if (predicate == null)
              return numbers.Where(num=> num % 2 == 0).ToList();
          else
              return numbers.Where(num=> num % 2 == 0 && predicate(num)).ToList();
      }
      

      或者,根据需要提供一个虚拟实现:

      public static List<int> Even(this List<int> numbers, Predicate<int> predicate = null)
      {
          predicate = predicate ?? new Predicate<int>(alwaysTrue);
          return numbers.Where(num=> num % 2 == 0 && predicate(num)).ToList();
      }
      

      另外,请考虑您是否真的想这样做。可选参数对编译代码的影响是调用代码现在提供了默认值,这意味着它将始终调用采用列表和委托的重载。

      如果您稍后想要返回,则需要确保重新编译所有调用该方法的代码,因为它不会神奇地开始使用不提供委托的方法。

      换句话说,这个调用:

      var even = list.Even();
      

      看起来是这样写的:

      var even = list.Even(null);
      

      如果您现在再次将方法更改为重载,如果上述调用没有重新编译,那么它将始终使用委托调用该方法,只需为该参数提供null

      【讨论】:

        【解决方案4】:

        您可以使用null-默认值:

        public static class Extended 
        {
            public static IEnumerable<int> Even(this IEnumerable<int> numbers, 
                                                Predicate<int> predicate = null)
            {
                if (predicate==null)
                {
                    predicate = i=>true;
                }
        
                return numbers.Where(num => num % 2 == 0 && predicate(num));
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2017-06-26
          • 2022-07-22
          • 1970-01-01
          • 2013-06-10
          • 2012-09-14
          • 2018-04-01
          • 1970-01-01
          • 2011-10-23
          • 2012-10-20
          相关资源
          最近更新 更多