16

次のようなデリゲートがあります。

public delegate bool ApprovalPrompt(ApprovalType type, int receipt, params string[] info);

この型のデリゲートを、呼び出したい関数へのパラメーターとして受け入れます。ただし、ある特定の呼び出し関数では、このデリゲートに一致する関数に追加のデータを渡したいと考えています。

実装関数の署名は次のとおりです。

private static bool LogApprovalNeeded(FraudFilterUtilities.ApprovalType type, int receipt, params string[] info)

次のように呼び出されます。

PrepareReceipt(LogApprovalNeeded);

私はそれが欲しい:

private static bool LogApprovalNeeded(Customer cust, FraudFilterUtilities.ApprovalType type, int receipt, params string[] info)

理想的には次のように使用されます。

PrepareReceipt(LogApprovalNeeded(myCustomer))

どうすればそのようなことを達成できますか? Customer1 つの関数とコールバックの間でパラメーターを保持するためだけに、クラスでフィールドを宣言する必要はありません...

4

6 に答える 6

25

ラムダを使用して、関数を「カリー化」できます。

PrepareReceipt((type, receipt, info) => 
    LogApprovalNeeded(myCustomer, type, receipt, info))

関数のカリー化は、関数への参照を格納するための正式な用語ですが、1 つ以上のパラメーターが「固定」されているため、メソッドのシグネチャが変更されます。

関数のシグネチャが、デリゲートが提供するすべての引数を必要としない場合にも、ラムダを使用できます。ラムダのすべての引数を転送しないことで、パラメーターを効果的に破棄できます。

于 2013-01-14T19:03:50.677 に答える
7

必要なものを達成するためにラムダを使用できます。

PrepareReceipt((type, receipt, info) =>
               LogApprovalNeeded(myCustomer, type, receipt, info));

LogApprovalNeededまたは、署名を次のように変更します。

static bool LogApprovalNeeded(ApprovalType type, int receipt, 
                              Customer cust = null, params string[] info)
{
}

ただし、 の後に可変数のパラメーターが既に定義されていることを考えると、少し混乱する可能性がありますcust

EDIT: Servyが正しく指摘したように、署名の変更により、説明したようにメソッドを呼び出すことができなくなります。ただし、関連するロジックを に移動するCustomerPrepareReceipt、上記のアプローチを使用する必要がなくなります (基本的に、新しい匿名メソッドを生成myCustomerし、クロージャーでラップします)。

于 2013-01-14T19:03:06.483 に答える
0

Lamba メソッドは完璧ではありません。属性がなく、コードが乱雑になります。
そのような方法を避けたい場合は、JavaScript の.bind()関数のような別の方法で行うことができます。
その関数は、拡張メソッドを持つ静的クラスを使用して、次のように C# に適合させることができます。

//This code requires the Nu-get plugin ValueTuple
using System.Diagnostics;

public static class Extensions
{

    [DebuggerHidden, DebuggerStepperBoundary]
    public static WaitCallback Bind(this Delegate @delegate, params object[] arguments)
    {
        return (@delegate, arguments).BoundVoid;
    }

    [DebuggerHidden, DebuggerStepperBoundary]
    public static Func<object, object> BindWithResult(this Delegate @delegate, params object[] arguments)
    {
        return (@delegate, arguments).BoundFunc;
    }

    [DebuggerHidden, DebuggerStepperBoundary]
    private static void BoundVoid(this object tuple, object argument)
    {
        tuple.BoundFunc(argument);
    }

    [DebuggerHidden, DebuggerStepperBoundary]
    private static object BoundFunc(this object tuple, object argument)
    {
        (Delegate @delegate, object[] arguments) = ((Delegate @delegate, object[] arguments))tuple;
        if (argument != null)
            if (!argument.GetType().IsArray)
                argument = new object[] { argument };
        object[] extraArguments = argument as object[];
        object[] newArgs = extraArguments == null ? arguments : new object[arguments.Length + extraArguments.Length];
        if (extraArguments != null)
        {
            extraArguments.CopyTo(newArgs, 0);
            arguments.CopyTo(newArgs, extraArguments.Length);
        }
        if (extraArguments == null)
            return @delegate.DynamicInvoke(newArgs);
        object result = null;
        Exception e = null;
        int argCount = newArgs.Length;
        do
        {
            try
            {
                if (argCount < newArgs.Length)
                {
                    object[] args = newArgs;
                    newArgs = new object[argCount];
                    Array.Copy(args, newArgs, argCount);
                }
                result = @delegate.DynamicInvoke(newArgs);
                e = null;
            } catch (TargetParameterCountException e2)
            {
                e = e2;
                argCount--;
            }
        } while (e != null);
        return result;
    }
}

これで、メソッド (ラムダではなく) のデリゲートを作成し、それにいくつかの固定パラメーターを割り当てることができます。

MessageBox.Show(new Func<double, double, double>(Math.Pow).BindWithResult(3, 2)(null).ToString()); //This shows you a message box with the operation 3 pow 2

したがって、以下のコードはWaitCallbackデリゲートを生成します。

new Func<double, double, double>(Math.Pow).Bind(3, 2)

以下のコードはFunc<object, object>デリゲートを生成します。

new Func<double, double, double>(Math.Pow).BindWithResult(3, 2)
于 2018-02-05T17:51:33.487 に答える
-3

関数を変更しPrepareReceiptて、追加のパラメーターを受け取ることができます。public void PrepareReceipt(Customer customer, ApprovalPrompt approvalPrompt)署名は、これを達成するために次のようになります。

于 2013-01-14T19:04:30.740 に答える
-4

デリゲートはCustomer型の引数を宣言していないため、そのデリゲートに渡すことはできません。「簡単な答え」は、新しい引数を取るために代理人の署名を変更することです。

とは言うものの、それはまた、代表者のすべての消費者の修正を必要とするでしょう。

于 2013-01-14T19:10:59.497 に答える