I want to pass a callback and its argument list(which has arbitrary numbers of arguments) to a register method, so I can call it later. Code be like:
public delegate void MyDelegate(params object[] args);
public void Register(MyDelegate callback, params object[] args);
public void InvokeOnNeed(); // Invoke stored callback with stored args
somewhere else:
private void foo(string foostring) {
...
}
Register(foo, "foo")
private void bar(int barint) {
...
}
Register(bar, 123)
How can I do this?
We can acheive that using a dictionary of delegates and parameters:
using System.Collections.Generic;
Dictionary<Delegate, object[]> Callbacks = new Dictionary<Delegate, object[]>();
Thus the registering method will be like:
void Register(Delegate callback, params object[] args)
{
if ( !Callbacks.ContainsKey(callback) )
Callbacks.Add(callback, args);
}
To allow multiple times the same delegate we need to use a Dictionary<Delegate, List<object[]>>
.
But to ensure the call in order of registration, it requires another table schema.
And the invoker:
public void InvokeOnNeed()
{
foreach ( var item in Callbacks )
item.Key.DynamicInvoke(item.Value);
}
Test
I used procedure and function to show that here we lost the returned result:
void Method1(string str)
{
Console.WriteLine(str);
}
int Method2(int a, int b)
{
Console.WriteLine(a + b);
return a + b;
}
Register((Action<string>)Method1, "test");
Register((Func<int, int, int>)Method2, 10, 20);
InvokeOnNeed();
The cast is required to have the code compiled.
Of course we can declare a delegate for the methods and cast to this type instead of using Action and Func, but it is weird and dirty:
delegate void MyDelegate(params object[] args);
static void Method1(params object[] args) { }
static void Method2(params object[] args) { }
Register((MyDelegate)Method1, "test");
Register((MyDelegate)Method2, 10, 20);
Indeed, to use that, the parameters part and the returned type of the signature must be exactly the same. Thus it only will be usefull if all methods requires params args as object or any same type.
Output
Test
30
To get results of methods
public IEnumerable<object> InvokeOnNeed()
{
foreach ( var item in Callbacks )
yield return item.Key.DynamicInvoke(item.Value);
}
var results = InvokeOnNeed();
int index = 1;
foreach (var item in results)
Console.WriteLine($"Result #{index++}: {item?.ToString() ?? "null"}");
Result #1: null
Result #2: 30