使用额外参数传递委托函数

时间:2013-01-14 19:00:16

标签: c# delegates

我有一个代表如下的代表:

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参数......

6 个答案:

答案 0 :(得分:21)

您可以使用lambda“咖喱”您的功能:

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

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

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

答案 1 :(得分:4)

您可以使用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正确地指出的那样,签名的更改不会让您按照您的描述调用该方法。但是,如果将与Customer相关的逻辑移动到PrepareReceipt,则不需要使用上述方法(它基本上生成一个新的匿名方法并在闭包中包装myCustomer

答案 2 :(得分:0)

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

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

在此示例中,第一个参数使用myCustomer值修复。此外,它还尝试在运行时协调参数类型。

答案 3 :(得分: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)

答案 4 :(得分:-3)

您可以更改PrepareReceipt功能以获取其他参数。签名看起来像public void PrepareReceipt(Customer customer, ApprovalPrompt approvalPrompt)来完成此任务。

答案 5 :(得分:-4)

您无法将其传递给该委托,因为委托未声明Customer类型的参数。 “简单的答案”是更改委托的签名以获取新的参数。

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