补充高阶函数

Oli*_*ini 11 c#

我正在尝试编写一个补充函数,以便在提供一个函数时f,它返回一个函数,在提供与相同的输入时f,它返回逻辑上相反的函数。

将类似的代码放入VS2017中后,我没有出现任何错误,但是我尚无法运行代码以查看其是否能按预期工作。我的意图是先在一个repl中尝试一下,看看它是否会按预期进行。我在那里使用的代码是这样的:

   public static Func<T, bool> Complement<T>(Func<T, bool> f)
   {
       return (T x) => !f(x);
   }

   public static bool GreaterThanTwo (int x) {
     return x > 2;
   }

   static public void Main(string[] args)
   {
     Func<int, bool> NotGreaterThanTwo = Complement(GreaterThanTwo);
     Console.WriteLine(NotGreaterThanTwo(1));
   }
Run Code Online (Sandbox Code Playgroud)

这是相同的链接。

在REPL中,出现错误:

main.cs(17,42):错误CS0411:无法从用法中推断方法`MainClass.Complement(System.Func)'的类型参数。尝试明确指定类型参数编译失败:1错误,0警告编译器退出状态1

我看了一些关于堆栈溢出的问题,这些问题涵盖了相同的错误消息,例如thisthis,但是我看不到它们与我遇到的这个问题有什么关系。

Pan*_*vos 9

Complement(GreaterThanTwo)正在尝试使用方法组,而不是Func<int,bool>委托。这失败了,因为Complement<T>需要通用委托。

该调用将使用进行编译Func<int,bool>,例如:

Func<int,bool> cmp= x=>x > 2;
var NotGreaterThanTwo = Complement(cmp);
Run Code Online (Sandbox Code Playgroud)

从方法组到委托之间存在隐式转换,这也意味着可行:

Func<int,bool> cmp= GreaterThanTwo;
var NotGreaterThanTwo = Complement(cmp);
Run Code Online (Sandbox Code Playgroud)

这就提出了一个问题,为什么原始代码不起作用?一个明确的投也可以工作:

var NotGreaterThanTwo = Complement((Func<int,bool>)GreaterThanTwo);
Run Code Online (Sandbox Code Playgroud)

方法组代表一重载方法,而不仅仅是单个方法。这意味着,编译器必须能够找到在任何情况下使用可用组。

其余的都是假设,因为我还没有找到有关此特定案例的明确参考或设计说明。

前两个方法组转换规则可能解释了问题所在:

  • 对应于形式为E(A)的方法调用(方法调用)选择单个方法M,并进行以下修改:

    • 参数列表A是一个表达式列表,每个表达式都分类为一个变量,并具有D的formal_parameter_list中相应参数的类型和修饰符(ref或out)。
    • 所考虑的候选方法仅是那些以其正常形式(适用的函数成员)适用的方法,而不是仅以其扩展形式适用的那些方法。
  • 如果方法调用算法产生错误,则会发生编译时错误。否则,该算法将产生具有与D相同数量的参数的单个最佳方法M,并认为存在转换。

由于Complement<T>(Func<T, bool> f)没有调用,因此编译器不知道组中要选择和转换的方法。它甚至不知道是什么T,因此不知道该组中的任何方法是否匹配。

另一方面,这可行:

var xx=new []{1,2,3}.Where(GreaterThanTwo);
Run Code Online (Sandbox Code Playgroud)

在这种情况下,Where的签名是:

public static System.Collections.Generic.IEnumerable<TSource> Where<TSource> (
    this System.Collections.Generic.IEnumerable<TSource> source, 
    Func<TSource,bool> predicate);
Run Code Online (Sandbox Code Playgroud)

并且type参数已经可以从中使用IEnumerable<TSource>

  • 我们开始同时发表评论,浏览相同的文档,并在大约相同的时间发布基本相同的答案:) (3认同)

SO *_*ood 9

什么是C#中的方法组?

方法组是一个名称的一套方法(可能是只有一个) -即理论上的ToString方法可以有多个重载(加任何扩展方法)ToString()ToString(string format)等-因此ToString本身是一个“方法组”。

使用时:

Func<int, bool> NotGreaterThanTwo = Complement(GreaterThanTwo);
Run Code Online (Sandbox Code Playgroud)

GreaterThanTwo是一个方法组。因此,这可能是正确的:

public static bool GreaterThanTwo (int x) {
  return x > 2;
}

// to make it clear this is something completely different
public static bool GreaterThanTwo (Action<bool, string, object> x) {
  return false;
}
Run Code Online (Sandbox Code Playgroud)

因此,编译器无法推断您所指的是哪种特定方法,因此您需要通过解决这种歧义来提供帮助。

您如何决定解决方案取决于您,但是这里至少有3种选择:

  1. 指定通用参数:

    Complement<int>(GreaterThanTwo);
    
    Run Code Online (Sandbox Code Playgroud)
  2. 将方法组隐式转换为所需的委托:

    Func<int, bool> greaterThanTwo = GreaterThanTwo; 
    var notGreaterThanTwo = Complement(greaterThanTwo);
    
    Run Code Online (Sandbox Code Playgroud)
  3. 明确将方法组转换为所需的委托:

    Complement((Func<int, bool>)GreaterThanTwo);
    
    Run Code Online (Sandbox Code Playgroud)