C# 无法使扩展方法在委托上工作

C# 无法使扩展方法在委托上工作,c#,delegates,extension-methods,C#,Delegates,Extension Methods,考虑下面的例子。如果首先定义该委托类型的变量,我可以调用委托的扩展方法。但是我不能对作为参数传递的委托调用扩展方法。我不明白为什么它第一次起作用,第二次却不起作用。我做错了什么 public static class Extender { public static Func<String, String> Compose(this Func<String, String> outer, Func<String, String> inner)

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

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 + ")")
        );
    }
}
公共静态类扩展器
{
公共静态函数组合(此函数外部,函数内部)
{
返回输入=>外部(内部(输入));
}
}
公开课演示
{
public void willingtotakestringdostringdelegate(函数映射)
{
//废话
}
public void RunMe()
{
Func outer=x=>“(外部:+x+”);
//这项工作:
var组合=外部组合(x=>“(内部:+x+”);
Trace.Write(组合(“!”);//-->(外部:(内部:!)
//这不起作用:
这是我的心愿(
(x=>“(外部:“+x+”))。组成(y=>“(内部:“+y+”))
);
}
}
更新 对于@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
    }
}
公共静态类CurryingHelper
{
公共静态函数应用(此函数有趣,A)
{
返回()=>乐趣(a);
}
公共静态函数应用(此函数有趣,A)
{
返回b=>fun(a,b);
}
公共静态函数应用(此函数有趣,A)
{
返回(b,c)=>乐趣(a,b,c);
}
公共静态函数应用(此函数有趣,A)
{
返回(b,c,d)=>乐趣(a,b,c,d);
}
//等等。。。
}
公开课演示
{
public void RunMe()
{
Func Func=(a,b,c)=>a-b+c;
var funcA1=函数应用(1);
Trace.Write(funcA1(2,3));//-->2
Trace.Write(functa1.Apply(2.Apply(3)();//-->2
}
}

这个概念并没有什么问题,只是在执行过程中出现了一些技术问题

关键是
x=>“(外部:“+x+”)
不是一个没有上下文的委托:它是一个lambda表达式,可以对应于委托(某种类型)甚至表达式树。因此,必须显式或隐式声明类型,例如

// this works:
this.WillingToTakeStringToStringDelegate(
    ((Func<string, string>)(x => "(outer: " + x + ")")).Compose(...)
);
//这是可行的:
这是我的心愿(
((Func)(x=>“(外部:“+x+”))。组成(…)
);
这与不能将lambda函数指定给隐式类型变量的原因完全相同,例如

var f1 = (string s) => "Hello " + s;                   // does not work
Func<string, string> f2 = (string s) => "Hello " + s;  // works fine
var f1=(字符串s)=>“Hello”+s;//不起作用
Func f2=(字符串s)=>“Hello”+s;//很好
C#中的Lambda表达式本身没有类型。例如,可以指定lambda表达式
x=>x!=0
谓词
Func
Func
YourCustomDelegate

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

示例:

  • 这很有效。提示是变量
    outer
    的类型

    Func<String, String> outer = x => "(outer: " + x + ")";
    
  • 这不起作用,因为没有为
    (x=>“(外部:+x+”)
    提供任何提示:


    • 其他答案是正确的;我只是想指出,设计团队故意选择扩展方法不适用于任何没有类型的表达式——因此,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();
      
      这不是标识、引用或装箱转换

      这里工作的语言设计原则是第一位的,没有令人讨厌的意外。扩展方法是对语言的一种后期添加,设计团队希望非常小心,不要添加它们以令人惊讶的方式变得适用的情况

      第二,在大多数情况下,从内部到外部的表达类型的原因。也就是说,当我们看到
      x=y
      时,我们独立地分析x和y的类型,然后决定该分配是否合法。但是对于倒装的无类型表达式。对于
      x=(y)=>{whater}
      我们分析x的类型,然后用它来决定
      (y)=>{whater}
      是否是合法的右手侧,如果是,它是什么类型,以及
      whater
      中的所有内容是什么类型。这种对事物正常顺序的反转导致编译器中出现一些非常复杂的代码,没有人急于添加另一种情况,即我们必须进行由内而外的推理

      最后,显然你对咖喱很感兴趣,这可能会引起你的兴趣


      好问题。我从来没有想到可以将扩展方法添加到Func中。感谢您的提问并向我介绍了一个新想法:)旁注:您可以通过使用其他两种类型(如
      double
      /
      int
      组合(此双v)
      1.Compose()
      @DavidArno,看看F,这都是关于函数的组合,在多年的命令式C中,这是非常令人费解的programming@philologon,您不需要使用扩展方法来执行此操作,请考虑:
      public Func CreatePartialApplication(Func-fun,bb,dd){return(a,c)=>fun(a,B,c,D)}
      我的意思是,如果扩展方法以这种方式工作,那就太好了,因为如果它们这样做了,那么我们就有了更好的语法来做同样的事情。然而,事实证明,它们只使用了一个“提示”,它非常难看,所以没有更好的语法。抱歉。@philologon,请查看问题的更新
      this.WillingToTakeStringToStringDelegate(
          (x => "(outer: " + x + ")").Compose(y => "(inner: " + y + ")")
      );
      
      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();