无法使扩展方法适用于委托

Tri*_*Gao 14 c# extension-methods delegates

考虑下面的例子.如果我首先定义该委托类型的变量,我可以调用委托的扩展方法.但我不能在作为参数传递的委托上调用该扩展方法.我不明白为什么它第一次工作,但第二次不起作用.我究竟做错了什么?

public static class Extender
{
    public static Func<String, String> Compose(this Func<String, String> outer, Func<String, String> inner)
    {
        return input => outer(inner(input));
    }
}
public class Demo
{
    public void WillingToTakeStringToStringDelegate(Func<String, String> map)
    {
        // blah
    }
    public void RunMe()
    {
        Func<String, String> outer = x => "(outer: " + x + ")";

        // this works:
        var composition = outer.Compose(x => "(inner: " + x + ")");
        Trace.Write(composition("!"));  // ---> (outer: (inner: !))

        // this doesn't work:
        this.WillingToTakeStringToStringDelegate(
            (x => "(outer: " + x + ")").Compose(y => "(inner: " + y + ")")
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

UPDATE

为@philologon

只要你不介意将lambda分配给变量然后是,你就可以使用这个方法创建函数的部分应用程序(currying),就像boss一样:

public static class CurryingHelper
{
    public static Func<X> Apply<A, X>(this Func<A, X> fun, A a)
    {
        return () => fun(a);
    }
    public static Func<B, X> Apply<A, B, X>(this Func<A, B, X> fun, A a)
    {
        return b => fun(a, b);
    }
    public static Func<B, C, X> Apply<A, B, C, X>(this Func<A, B, C, X> fun, A a)
    {
        return (b, c) => fun(a, b, c);
    }
    public static Func<B, C, D, X> Apply<A, B, C, D, X>(this Func<A, B, C, D, X> fun, A a)
    {
        return (b, c, d) => fun(a, b, c, d);
    }

    // etc... 
}

public class Demo
{
    public void RunMe()
    {
        Func<Int32, Int32, Int32, Int32> func = (a, b, c) => a - b + c;
        var funcA1 = func.Apply(1);
        Trace.Write(funcA1(2, 3));               // --> 2
        Trace.Write(funcA1.Apply(2).Apply(3)()); // --> 2
    }
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*Jon 12

概念没有任何问题,只有执行中的一些技术问题.

的一点是,x => "(outer: " + x + ")"没有上下文委托:它是一个lambda表达式,既可以对应于(一个代表一些类型)或甚至表达式树.因此,必须明确或隐式声明类型,例如

// this works:
this.WillingToTakeStringToStringDelegate(
    ((Func<string, string>)(x => "(outer: " + x + ")")).Compose(...)
);
Run Code Online (Sandbox Code Playgroud)

这就是为什么你不能将lambda函数分配给隐式类型变量的完全相同的原因,例如

var f1 = (string s) => "Hello " + s;                   // does not work
Func<string, string> f2 = (string s) => "Hello " + s;  // works fine
Run Code Online (Sandbox Code Playgroud)


dtb*_*dtb 10

C#中的Lambda表达式本身没有类型.例如,您可以分配lambda表达式x => x != 0Predicate<int>,Func<int, bool>,Func<long, bool>YourCustomDelegate.

因此,无论何时使用lambda表达式,都需要向编译器提供应该使用的委托类型的提示.

例子:


Eri*_*ert 7

其他答案都是正确的; 我只想注意,设计团队故意选择扩展方法不适用于任何没有类型的表达式 - 因此,lambdas,匿名方法,null或方法组或任何动态表达式都没有扩展方法.

事实上它比这更远; 点左侧的表达式必须通过标识,隐式引用装箱转换转换为第一个参数.换句话说:

enum E { }
static class Ext
{
    public static E X(this E e) { return e; }
}

// Legal
E e1 = 0;
// Legal
E e2 = e1.X();
// No way José.
E e3 = 0.X();
Run Code Online (Sandbox Code Playgroud)

这不是身份,参考或拳击转换.

这里的语言设计原则是首先,没有令人讨厌的惊喜.扩展方法是语言的后期补充,设计团队希望在不添加可能以令人惊讶的方式适用的情况时非常谨慎.

第二,在大多数情况下,C#从内到外的表达类型的原因.也就是说,当我们看到x = y我们独立分析x和y的类型,然后决定分配是否合法.但对于倒置的无类型表达式.因为x = (y)=>{whatever}我们分析x的类型,然后使用它来决定是否(y)=>{whatever}是合法的右手边,如果是,它是什么类型,以及内部的内容whatever是什么类型.正常顺序的反转导致编译器中的一些非常复杂的代码,并且没有人急于添加另一个我们必须进行内向外推理的情况.

最后,因为显然你对currying感兴趣,这可能是你感兴趣的.

http://blogs.msdn.com/b/ericlippert/archive/2009/06/25/mmm-curry.aspx