繁体   English   中英

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

[英]Passing delegate function with extra parameters

我有一个代表,如下所示:

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

你可以使用lambda“curry”你的函数:

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

卷曲函数是用于存储对函数的引用但是具有一个或多个参数“固定”的形式术语,因此改变了该方法的签名。

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

您可以使用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包装在一个闭包中。

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

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

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

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)

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM