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))

我怎样才能完成这样的事情?我宁愿不需要在类中声明一个字段只是为了保存Customer一个函数和回调之间的参数......

4

6 回答 6

25

您可以使用 lambda 来“咖喱”您的函数:

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

对函数进行柯里化是存储对函数的引用但具有一个或多个“固定”参数的正式术语,从而改变了方法的签名。

当您的函数签名不需要委托提供的所有参数时,您也可以使用 lambda;您可以通过不传递 lambda 中的所有参数来有效地丢弃参数。

于 2013-01-14T19:03:50.677 回答
7

您可以使用 lambda 来实现您所需要的。

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

或者,将您的LogApprovalNeeded签名更改为:

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

但考虑到您已经在cust.

编辑:正如 Servy 正确指出的那样,签名的更改不会让您按照您的描述调用该方法。但是,如果您将与相关的逻辑移动CustomerPrepareReceipt,则不需要使用上述方法(它基本上会生成一个新的匿名方法并包装myCustomer在一个闭包中。

于 2013-01-14T19:03:06.483 回答
1

如果您需要委托部分应用程序的通用解决方案(减少参数),请查看NReco Commons开源库,它包含可以对任何委托类型执行此操作的 PartialDelegateAdapter:

var logApprovalForCustomer = (new PartialDelegateAdapter(LogApprovalNeeded,
    new[] {myCustomer})).GetDelegate<Func<FraudFilterUtilities.ApprovalType,int,string[],bool>>();

在此示例中,第一个参数固定为 myCustomer 值。此外,它还尝试在运行时协调参数类型。

于 2014-07-07T16:49:51.487 回答
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;
    }
}

现在您可以为您的方法(不是 lambda)创建一个委托并为其分配一些固定参数:

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

您不能将其传递给该委托,因为委托未声明客户类型的参数。“简单的答案”是更改委托的签名以采用新的参数。

也就是说,这也需要修改代表的所有消费者。

于 2013-01-14T19:10:59.497 回答