14

以下の例を考えてみましょう。最初にデリゲート型の変数を定義すると、デリゲートの拡張メソッドを呼び出すことができます。しかし、引数として渡されるデリゲートでその拡張メソッドを呼び出すことはできません。最初はうまくいくのに、2回目はうまくいかない理由がわかりません。私は何を間違っていますか?

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 + ")")
        );
    }
}

アップデート

@philologonのために

ラムダを変数に代入することを気にしない限り、この方法を使用して、上司のように関数の部分的な適用 (カリー化) を作成できます。

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
    }
}
4

3 に答える 3

13

構想には何の問題もありませんが、実行には技術的な問題がいくつかあるだけです。

ポイントは、コンテキストのないデリゲートでx => "(outer: " + x + ")"ないということです。これは、(何らかの型の) デリゲートまたは式ツリーに対応するラムダ式です。したがって、型は明示的または暗黙的に宣言する必要があります。

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

これは、ラムダ関数を暗黙的に型付けされた変数に割り当てることができないのとまったく同じ理由です。

var f1 = (string s) => "Hello " + s;                   // does not work
Func<string, string> f2 = (string s) => "Hello " + s;  // works fine
于 2013-10-22T18:11:05.710 に答える
11

C# のラムダ式自体には型がありません。たとえば、ラムダ式x => x != 0Predicate<int>Func<int, bool>Func<long, bool>またはに割り当てることができますYourCustomDelegate

したがって、ラムダ式を使用するときはいつでも、使用するデリゲート型のヒントをコンパイラに提供する必要があります。

例:

  • これは機能します。ヒントは変数の型ですouter

    Func<String, String> outer = x => "(outer: " + x + ")";
    
  • これは機能します。ヒントはinnerComposeメソッドのパラメーターの型です。

    var composition = outer.Compose(x => "(inner: " + x + ")");
    
  • 次のヒントが提供されていないため、これは機能しません(x => "(outer: " + x + ")")

    this.WillingToTakeStringToStringDelegate(
        (x => "(outer: " + x + ")").Compose(y => "(inner: " + y + ")")
    );
    
于 2013-10-22T18:11:18.767 に答える