19

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

5 Answers 5

32

You can use a lambda to "curry" your function:

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.

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.

Sign up to request clarification or add additional context in comments.

3 Comments

I knew it had to be possible somehow. Thanks!
and what does the signature of PrepareReceipt look like?
@mcmillab The parameter is ApprovalPrompt.
7

You can use a lambda to achieve what you need.

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

Alternatively, change your LogApprovalNeeded signature to:

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.

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.

3 Comments

@Servy Agreed, wouldn't work in the way the author described, I'll point it out. If he can encapsulate the logic involving Customer inside of PrepareReceipt, though, he won't need to curry his call.
+1 - Good answer, and I appreciate the alternative (although I definitely agree about it being confusing), but I'm going to accept Servy's, because he also included the official terminology for this.
@Eve - I really wish I could encapsulate it, but because of the mix of old code and new code we have, PrepareReceipt can't work with the Customer object. Or at least, it can't do so usefully.
1

If you need generic solution for delegates partial application (parameters reduction) take a look to the NReco Commons open source library, it contains PartialDelegateAdapter that can do that for any delegate type:

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.

Comments

-3

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.

1 Comment

If the function doesn't need this parameter then that's not the appropriate thing to do. It will at best just cause confusion everywhere else it's used.
-5

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.

That said, that would also require modification of all the consumers of the delegate.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.