函数的功能只能单向运行,不能双向。

6

我正在尝试编写一个函数,其中一个参数是另一个函数——这是我以前做过很多次的任务。下面的代码可以正常工作:

int RunFunction(Func<int,int> f, int input) {
    return f(input);
}
int Double(int x) {
    return x*2;
}

// somewhere else in code
RunFunction(Double,5);

然而,这并不起作用:

public static class FunctionyStuff {
    public static int RunFunction(this Func<int,int> f, int input) {
        return f(input);
    }
}

// somewhere else in code
Double.RunFunction(5);

有什么想法,为什么第一个有效而第二个无效?
2个回答

5

第一个版本在“参数到参数”匹配的过程中执行了方法组转换。这种转换不适用于扩展方法。对于lambda表达式也是如此-你不能写:

((int x) = > x * 2).RunFunction(10);

请注意。本文涉及的是IT技术相关内容。

C# 4规范的第7.6.5.2节详细介绍了扩展方法调用的细节。它首先要求方法调用必须是以下形式之一:

expr.identifier ( )
expr.identifier ( args )
expr.identifier < typeargs > ( )
expr.identifier < typeargs > ( args )

在这个规则中,表达式(expr)的类型被用于以下情况:

如果满足以下条件之一,则扩展方法Ci.Mj可用的

  • [...]
  • expr到Mj的第一个参数的类型存在隐式的标识、引用或装箱转换。

经过注释的版本还包括Eric Lippert的评论:

此规则确保对扩展double的方法不会同时扩展int。它还确保没有在匿名函数或方法组上定义扩展方法。


也许你应该解释一下为什么对于扩展方法和 Lambda 表达式不会发生这种情况。因为它们没有特定的委托类型。 Func<int, int> 和一个假设的 MyIntIntDelegate 可能具有相同的签名,但是它们是不同的类型,所以编译器无法知道应该转换哪一个。 - Timwi
所以,如果我理解正确的话,在第一个版本中,从MethodFunc有一个隐式转换吗? - Joe
@Joe:这比那更微妙。方法组转换不是标识、引用或装箱转换。请看我的编辑。 - Jon Skeet
我认为这个解释在技术上是完全正确的,但对于那些没有阅读C#规范的人来说很难理解。我仍然认为它应该提到“方法组”和“匿名函数”是特殊的编译时类型,它们本身并不是自动委托类型。可以通过展示您可以在委托上调用扩展方法,例如new Func<int, int>(x => 2*x).MyExtensionMethod(),以演示必须先从lambda转换为委托类型。 - Timwi
@Timwi:我希望那些试图向委托类型添加扩展方法的人已经对方法组有所了解。也许我太乐观了... - Jon Skeet

0

扩展方法 "被调用时就像它们是扩展类型的实例方法一样"。

在你的情况下,Double 不是一个实例,所以你不能在其上调用扩展方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接