3

C# では、より高次の関数を作成することができます。関数gを引数として取る関数。f関数を指定し、その機能を拡張する別の関数を返すような関数を作成したいとします。返された拡張メソッドの引数名を定義するにはどうすればよいですか? 動機は、私が一般的に高次のメソッドを扱っていることであり、その中には新しいメソッドを生成するものもあります..そして、これらにはパラメーター名などが付けられていないため、使用が難しい場合があります。

gC# でおよびfそれぞれを定義する方法を示す例:

Tを引数として取り、 を返すメソッドを拡張できるメソッド Extend を定義しますS

static class M
{
    public static Func< T, S> Extend(Func< T, S> functionToWrap)
    {
      return (someT) =>
      {
        ...
        var result = functionToWrap(someT);
        ...
        return result;
      };
    }
}

その後、メソッドを変更せずにクラスのメソッドを拡張できます。

class Calc2
{
    public Func< int, int> Calc;
    public Calc2()
    {
      Calc = M.Extend< int, int>(CalcPriv);
    }
    private int CalcPriv(int positiveNumber)
    {
      if(positiveNumber < 0) throw new Exception(...);
      Console.WriteLine("calc " + i);
      return i++;
    }
}

残念ながら、利用可能な情報は のみであるため、引数名positiveNumberは利用できなくなりましたFunc<int, int> Calc。それは、入力して拡張メソッドを使用するときですnew Calc2().Calc(-1)。実際、私の引数が間違っているという IDE からの助けは得られません。

a を定義してこれにキャストできればいいのですdelegateが、これは不可能です。

助言がありますか?

4

3 に答える 3

2

名前付きパラメーターを持つ固定のデリゲート タイプのみが必要な場合は、独自のデリゲート タイプを定義するだけです。

Func は次のように定義されています。

public delegate TResult Func<in T, out TResult>(T arg)

したがって、必要なパラメーター名を使用して独自のデリゲート型を定義できます。

しかし、あなたの例では、渡されたデリゲート型を保持したいので、ここでは機能しません。理論的には、次のように関数を定義できます。

public static T Extend(T functionToWrap)
{
}

残念ながら、入力タイプを適切な署名を持つデリゲートに制限する適切な一般的な制約はありません (または、デリゲートだけでも)。しかし、これらの制約がなければ、実装は非常に醜くなり、静的な型の安全性が大幅に失われるため、IMO の価値はありません。

1 つの回避策を使用しています。

new MyFunc(Extend(f))

ここで、MyFunc は必要なパラメーター名を定義します。

または、次のようにすることもできます。

public static T ConvertDelegate<T>(Delegate d)
{
    if (!(typeof(T).IsSubclassOf(typeof(Delegate))))
        throw new ArgumentException("T is no Delegate");
    if (d == null)
        throw new ArgumentNullException();
    MulticastDelegate md = d as MulticastDelegate;
    Delegate[] invList = null;
    int invCount = 1;
    if (md != null)
        invList = md.GetInvocationList();
    if (invList != null)
        invCount = invList.Length;
    if (invCount == 1)
    {
        return (T)(object)Delegate.CreateDelegate(typeof(T), d.Target, d.Method);
    }
    else
    {
        for (int i = 0; i < invList.Length; i++)
        {
            invList[i] = (Delegate)(object)ConvertDelegate<T>(invList[i]);
            }
            return (T)(object)MulticastDelegate.Combine(invList);
        }
    }

public static TDelegate Extend<TDelegate,TArg,TResult>(Func<TArg,TResult> functionToWrap)
where TDelegate:class
    {       
        Func<TArg,TResult> wrappedFunc= DoTheWrapping(functionToWrap);
        return ConvertDelegate<TDelegate>(wrappedFunc);
    }

ところで、ConvertDelegate 関数を使用して、.net 4 より前でもデリゲートの Co/Contravariance を取得できます。

于 2010-10-28T21:48:35.177 に答える
1

新しく構築されたデリゲートを基になるFuncデリゲートメソッドに動的にバインドすることにより、名前付きパラメーターを使用してデリゲートにキャストすることができます。

public delegate double CalcFunc(double value);

static class M
{
    public static Func<T, S> Extend<T,S>(Func<T, S> functionToWrap)
    {
      return (someT) => functionToWrap(someT);
    }
}

class Program
{
    private static double Calc(double input)
    {
        return 2*input;
    }

    [STAThread]
    static void Main()
    {
        Func<double, double> extended = M.Extend<double, double>(Calc);

        CalcFunc casted = (CalcFunc)Delegate.CreateDelegate(typeof(CalcFunc), extended.Target, extended.Method);
        Console.WriteLine(casted(2) + " == 4");
        Console.WriteLine("I didn't crash!");
        Console.ReadKey();
    }
}

警告の一言:これはキャストのコンパイル時チェックを行いません。シグニチャが完全に一致しない場合、実行時にバインドが失敗します(.NET 4での共変性の特別なサポートを除く)。

于 2010-10-28T21:54:56.893 に答える
0

これが匿名デリゲートの優れた点です。彼らは匿名です。Func は、int を受け取り、int を返すメソッドへのデリゲートです。関数が実際に行うこと、したがってパラメーターの名前は関係ありません。

これが機能する唯一の方法は、Calc が CalcPriv と同じ署名で定義された名前付きデリゲート型である場合です。匿名の拡張機能を含め、すべて記述どおりに機能しますが、Calc の名前付きパラメーターが必要になります。

情報を伝える別の方法は、///<summary></summary>Calc が受け取るように設計されたパラメーターを説明するタグを付けて xml-doc Calc に送信することです。

最後に、から派生してクラスFunc<T,TResult>を作成できます。TakesPositiveInteger<T,TResult>これは少し先のことですが、自己文書化コードについて話したい場合は...

于 2010-10-28T21:25:22.577 に答える