What is the best way to pass parameters through to a delegate? I can see benefits for both of the following ways so I would like to know the most used and industry accepted way of doing it:
1) Pass any parameters individually with each parameters being just that, a parameter.
Example Delegate
public delegate void MyDelegate(bool PARAM1, String PARAM2, int PARAM3);
2) Pass any parameters through a struct and the only parameter of the delegate is that struct.
Example Struct
public struct MyDelegateArgs
{
public bool PARAM1;
public String PARAM2;
public int PARAM3;
}
Example Delegate
public delegate void MyDelegate(MyDelegateArgs args);
Imagine your struct had 20 properties / fields - would you really want to pass all of those in as parameters? Generally I would argue to hide all of this complexity and keep your code DRY by passing in a class / struct instead - this in most cases is also more expressive - your delegates needs a parameter of type MyDelegateArgs.
The only argument I could come up with for passing in individual values is if you only need a small subset that just happen to be used in MyDelegateArgs but are otherwise unrelated.
If your delegate has utility as an Action<T> or a Predicate<T>, using the struct argument is preferable as it will keep you from tripping over a case where the signature gets too large for the predefined Action or Predicate definitions when you go to extend the delegate's signature.
Related
I don't know if this is possible in C#. But here's what I'm trying to do:
An interface that declares a method with a completion block / callback / lambda / whatever you want to call it. Seems simple enough. But, here's a first attempt:
namespace N {
interface MyInterface {
delegate void CompletionBlock();
void doSomething(CompletionBlock completionBlock);
}
}
But interfaces can't declare delegates. Ok, how about this?
namespace N {
public delegate void CompletionBlock();
interface MyInterface {
void doSomething(CompletionBlock completionBlock);
}
}
Sure that works, but now, say I declare another type of CompletionBlock, one which takes an argument for instance. I can't call the type CompletionBlock because it'll clash with the above type, if inside the same namespace.
Then there's Action and Func. But even Action takes a parameter, and I want no parameters. So Action doesn't seem like a viable candidate. Really, all I want, is a way of inlining the concept of "anonymous block as long as the signature is void(void). Something like this:
namespace N {
interface MyInterface {
void doSomething(typeof(()=>) completionBlock);
}
}
But lambdas don't really have a type in C#. Do they at least have signatures?
Basically, is there any way around this?
In Objective-C, for instance, blocks are of type NSBlock, even though their signatures themselves are not easily accessible, but one can create a method that takes a block with a specific signature. For instance:
- (void)doSomething:(void(^)(void))completionBlock
Would be a method that takes a block with no parameters and no return type, and you'd call the method like so, for instance:
[obj doSomething:^(){your block code}];
Then there's Action and Func. But even Action takes a parameter, and I want no parameters. So Action doesn't seem like a viable candidate.
If you use Action (not Action<T>), it does not take parameters.
namespace N {
interface MyInterface {
void doSomething(Action completionBlock);
}
}
This allows you to write:
yourInstance.doSomething(() => DoWhatever(foo));
Or, if DoWhatever is void DoWhatever(), just:
yourInstance.doSomething(DoWhatever);
Use Action<T> or Func<TResult> or one of their siblings or just Action in your case.
Action<...> don't return anything where as Func<..., TResult> do.
I have a bit interresting problem, i wasn't able to find solution. I have 4 different delegates and want to use junt one of them as parameter of function. But the only solution, I found is :
void doSomething(Delegate delegate){ }
but I don't want to be able pass every delegate but just one of these:
public delegate void CopyStatusDelegate(long fileSize, long copied);
public delegate void CopyProgressDelegate(int progress);
public delegate void CopyEstimatedDelegate(int copyTimeSeconds, int estimatedTimeSeconds);
public delegate void CopyAllInfoDelegate(long fileSize, long copied, int progress, int copyTimeSeconds, int estimatedTimeSeconds);
Is in c# any else solution except of throwing an Exception when input delegate isn't one of these?
Write four overloads of doSomething, each of which accepts one of these delegates. The compiler will prevent you from passing in anything else, and if you need to for some reason (it's not very clear) you can forward from these overloads to a protected or private method that accepts any delegate.
I am new to C# and have two questions about delegates.
MSDN shows that the Delegate class only has two constructors, both which take two arguments each. However, in the program below, I am instantiating a Delegate object with a seemingly one-parameter constructor (see the commented line).
using System;
static class Program
{
public delegate void MyDelegate();
static void MyMethod()
{
Console.WriteLine("I'm inside MyMethod!");
}
static void Main()
{
MyDelegate myDelegate = new MyDelegate(MyMethod); // Constructor Call
myDelegate();
}
}
Am I misunderstanding something?
My second question is: when declaring delegates with one or more parameters, why do we need to give the parameters names? In C++, we could do something like this inside classes:
public delegate void MyOtherDelegate(int, int, string);
It may look like you're calling a standard constructor, but the C# compiler is actually doing a little slight of hand with your code. When you write
MyDelegate myDelegate = new MyDelegate(MyMethod);
You're not actually invoking any of the constructors listed on MSDN directly. It instead calls a constructor that the compiler has automatically defined on the delegate type MyDelegate (these constructors are not shown on MSDN). In fact, the constructors that are listed on MSDN cannot even be called directly:
This constructor cannot be used in application code. To create a delegate by specifying the name of an instance method, use an overload of the CreateDelegate method…
It's worth noting that you can even do this for brevity:
MyDelegate myDelegate = MyMethod;
As for why parameter names are necessary, perhaps they may not be strictly necessary for compilation in general, but I suspect that the designers of the CLR wanted to be consistent with other features of the in the run-time. It allows you to do things like specifying explicit parameter names that you wouldn't be able to do otherwise:
Func<int, int> foo = x => x + 1;
foo(arg: 1);
It also means you can inspect the parameter names through reflection and potentially do some useful processing with them.
Not exactly answering your question but just so you are aware, C# also supports Func delegates. in which case you don't need to even declare anything.
e.g. This method accepts a delegate with a parameter of type string and output of Int
public static void DoSomethingWithDelegate(Func<string, Int32> converstionDelegate)
{
int x = converstionDelegate("1");
}
Read more about it here: http://msdn.microsoft.com/en-us/library/bb549151.aspx
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...
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.
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.
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.
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.
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.
I want to implement a method that will find stuff in my custom class. It should work like generic collections work - i pass a pointer to a function, and the method will iterate through all it has to look in, apply this function, and if it returns true return the found item.
I'd like to pass function pointer as a parameter, but i dont want to declare delegate types.
I know i can do something like:
delegate bool Foo(MyClass)
MyClass MyMethod(Foo x)
{...}
And i know i can do something like this:
MyClass MyMethod(Func<MyClass,bool> x)
But can i do it without declaring a delegate type and without using built in stuff like Func<> which has limits on how many parameters i can have (in case of Func, one...)
You can just use delegate if you want, although it's a bit old school :)
public void TestInvokeDelegate()
{
InvokeDelegate( new TestDelegate(ShowMessage), "hello" );
}
public void InvokeDelegate(TestDelegate del, string message)
{
del(message);
}
public delegate void TestDelegate(string message);
public void ShowMessage(string message)
{
Debug.WriteLine(message);
}
You can allways pass in a Delegate and call DynamicInvoke on it:
MyClass MyMethod(Delegate x) {
// ...
x.DynamicInvoke(....);
// ...
}
It looks like you are trying to implement the Visitor pattern. In this case visiting methods usually have only one parameter - the instance to visit. Having additional arguments passed around conceals the use of the pattern and makes it harder to reason about. This article shows you one way to implement it in C#.
The key is to create a visitor class that will encapsulate all the parameters that affect the visiting process. This way you don't need to pass anythnig other than an object in question in the visiting method - everything else lives in instance fields.
However, if you really want to pass some additional parameters in the method and don't know what type they can have, there are ways to do that. More or less standard approach in .NET world is to use a delegate without return value and with single parameter of type object, the example would be ParameterizedThreadStart delegate:
public delegate void ParameterizedThreadStart(
Object obj
)
This way you get to pass only one parameter in the delegate, but it could be anything - an instance of a class, an array or null, if you end up not needing additional arguments after all. The downside of this approach is that it requires type casting which can lead to runtime errors.