C# 传递带有额外参数的委托函数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14324803/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Passing delegate function with extra parameters
提问by Bobson
I have a delegate which looks like the following:
我有一个如下所示的代表:
public delegate bool ApprovalPrompt(ApprovalType type, int receipt, params string[] info);
I accept a delegate of this type as a parameter to the function I want to call. However, in one particular calling function, I want to pass some extra data to the function which matches this delegate.
我接受这种类型的委托作为我要调用的函数的参数。但是,在一个特定的调用函数中,我想将一些额外的数据传递给与此委托匹配的函数。
Here's the signature of the implementing function:
这是实现函数的签名:
private static bool LogApprovalNeeded(FraudFilterUtilities.ApprovalType type, int receipt, params string[] info)
and it's being called as follows:
它被称为如下:
PrepareReceipt(LogApprovalNeeded);
I'd like it to be:
我希望它是:
private static bool LogApprovalNeeded(Customer cust, FraudFilterUtilities.ApprovalType type, int receipt, params string[] info)
which ideally would be used as follows:
理想情况下将按如下方式使用:
PrepareReceipt(LogApprovalNeeded(myCustomer))
How can I accomplish such a thing? I'd rather not need to declare a field in the class just to hold the Customer
parameter between one function and the callback...
我怎样才能完成这样的事情?我宁愿不需要在类中声明一个字段来保存Customer
一个函数和回调之间的参数......
采纳答案by Servy
You can use a lambda to "curry" your function:
您可以使用 lambda 来“咖喱”您的函数:
PrepareReceipt((type, receipt, info) =>
LogApprovalNeeded(myCustomer, type, receipt, info))
Currying a function is the formal term for storing a reference to a function but with one or more of the parameters "fixed", thus altering the signature of the method.
Currying 函数是一种正式术语,用于存储对函数的引用,但具有一个或多个“固定”参数,从而改变方法的签名。
You can also use a lambda when the signature of your function doesn't need all of the arguments that the delegate is providing; you can effectively discard parameters by not passing forward all of the arguments in the lambda.
当您的函数签名不需要委托提供的所有参数时,您也可以使用 lambda;您可以通过不传递 lambda 中的所有参数来有效地丢弃参数。
回答by Mir
You can use a lambda to achieve what you need.
您可以使用 lambda 来实现您的需求。
PrepareReceipt((type, receipt, info) =>
LogApprovalNeeded(myCustomer, type, receipt, info));
Alternatively, change your LogApprovalNeeded
signature to:
或者,将您的LogApprovalNeeded
签名更改为:
static bool LogApprovalNeeded(ApprovalType type, int receipt,
Customer cust = null, params string[] info)
{
}
But it could get a bit confusing, considering that you already have a variable number of parameters defined after cust
.
但考虑到您已经在cust
.
EDIT:As Servy rightfully pointed out, the change of signature won't let you call the method as you described. If you move the logic related to Customer
to PrepareReceipt
, though, you won't need to use the above approach (which basically generates a new anonymous method and wraps myCustomer
in a closure.
编辑:正如 Servy 正确指出的那样,签名的更改不会让您按照您的描述调用该方法。如果移动相关的逻辑Customer
来PrepareReceipt
,不过,你不需要使用上面的方法(这基本上产生新的匿名方法和包装myCustomer
在一个封闭。
回答by Ameen
You can change the PrepareReceipt
function to take an additional parameter. The signature would look something like public void PrepareReceipt(Customer customer, ApprovalPrompt approvalPrompt)
to accomplish this.
您可以更改PrepareReceipt
函数以采用附加参数。签名看起来像public void PrepareReceipt(Customer customer, ApprovalPrompt approvalPrompt)
完成这个。
回答by JerKimball
You can't pass it to that delegate as the delegate does not declare an argument of type Customer. The "simple answer" would be to change the signature of the delegate to take the new argument.
您不能将其传递给该委托,因为该委托未声明 Customer 类型的参数。“简单的答案”是更改委托的签名以采用新参数。
That said, that would also require modification of all the consumers of the delegate.
也就是说,这还需要修改委托的所有消费者。
回答by Vitaliy Fedorchenko
If you need generic solution for delegates partial application (parameters reduction) take a look to the NReco Commonsopen source library, it contains PartialDelegateAdapter that can do that for any delegate type:
如果您需要委托部分应用程序(参数减少)的通用解决方案,请查看NReco Commons开源库,它包含可以为任何委托类型执行此操作的 PartialDelegateAdapter:
var logApprovalForCustomer = (new PartialDelegateAdapter(LogApprovalNeeded,
new[] {myCustomer})).GetDelegate<Func<FraudFilterUtilities.ApprovalType,int,string[],bool>>();
in this example 1st parameter is fixed with myCustomer value. In addition it also tries to harmonize argument types in runtime.
在本例中,第一个参数固定为 myCustomer 值。此外,它还尝试在运行时协调参数类型。
回答by Davide Cannizzo
Lamba methods aren't perfect: they don't have attributes and they contribute for a messy code.
If you want to avoid that kind of methods, you can do it in an alternative way, which is just like JavaScript's .bind()
function.
That function can adapted in C# as follow, using a static class with some extension method:
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;
}
}
Now you can create a delegate for your method (not lambda) and assign to it some fixed parameters:
现在您可以为您的方法(不是 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
So, the below code will produce a WaitCallback
delegate:
所以,下面的代码将产生一个WaitCallback
委托:
new Func<double, double, double>(Math.Pow).Bind(3, 2)
Whereas the below code will produce a Func<object, object>
delegate:
而下面的代码将产生一个Func<object, object>
委托:
new Func<double, double, double>(Math.Pow).BindWithResult(3, 2)