我有一个代表,它看起来像下面:
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来“讨好”你的函数:
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共享开源库,它包含PartialDelegateAdapter能够这样做的任何委托类型:
var logApprovalForCustomer = (new PartialDelegateAdapter(LogApprovalNeeded,
new[] {myCustomer})).GetDelegate<Func<FraudFilterUtilities.ApprovalType,int,string[],bool>>();
在这个例子中第一参数是固定的myCustomer值。 另外它还试图在运行时协调的参数类型。
兰巴方法并不完美:他们没有属性和他们为乱码贡献。
如果你想避免那种方法,你可以用另一种方式,它就像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)
您可以更改PrepareReceipt
功能采取额外的参数。 签名会看起来像public void PrepareReceipt(Customer customer, ApprovalPrompt approvalPrompt)
做到这一点。
你不能把它传递给该委托的委托不声明类型客户的参数。 “简单的回答”是改变了代表的签名采取新的参数。
尽管如此,这也将要求所有消费者的委托的修改。