It's quite simple if you use fixed number of different delegates with the signatures defined in advance. Each delegate type will have fixed number of parameters (you can also use
params
syntax to mimic unlimited number of parameters of the same type, but this is only a syntactic candy). Of course, it will include the usual polymorphism in parameter classes. You can also create and use generic delegates, not only those covered by
System.Action
or
System.Func
.
In this case, the usage will not require any type cast, so it won't be an invitation for bugs and mess-ups. If you want to expose more "dynamic" indirection feature, I would say, it wouldn't worth the effort and you should reconsider you approach.
Consider this:
delegate void MySimpleDelegate(int a, string b, DelegateUser user);
delegate void MySimpleDelegateWithParamsArray(DelegateUser user, int a, params string[] b);
delegate int MyDelegateWithByRef(int a, ref string b);
delegate int MyDelegateWithOut(int a, string b, out string c);
delegate void MyGenericDelegate<FIRST, SECOND>(
DelegateUser user,
FIRST first,
SECOND second);
class DelegateUser {
internal void Call(MySimpleDelegate[] methods, int a, string b) {
foreach (MySimpleDelegate methodInstance in methods)
methodInstance(a, b, this);
}
internal void Call(MySimpleDelegateWithParamsArray[] methods, int a, params string[] b) {
foreach (MySimpleDelegateWithParamsArray methodInstance in methods)
methodInstance(this, a, b);
}
internal void Call(MyDelegateWithByRef method, int a, ref string b) {
method(a, ref b);
}
internal void Call(MyDelegateWithOut method, int a, string b, out string c) {
method(a, b, out c);
}
internal void Call<FIRST, SECOND>(
MyGenericDelegate<FIRST, SECOND>[] methods,
DelegateUser user, FIRST first, SECOND second) {
foreach (MyGenericDelegate<FIRST, SECOND> methodInstance in methods)
methodInstance(this, first, second);
}
internal string Value { get; set; }
}
class Test {
void TestCalls() {
DelegateUser user = new DelegateUser();
user.Call<int, double>(new MyGenericDelegate<int, double>[] {
delegate(DelegateUser userRef, int iValue, double dValue) {
user.Value =
string.Format("Firt: {0}: {1}, {2}",
user.Value, iValue, dValue);
},
delegate(DelegateUser userRef, int iValue, double dValue) {
user.Value =
string.Format("Second: {0}: {1}, {2}",
user.Value, iValue, dValue);
}
}, user, 3, 3d);
}
}
This class makes no sense, it's only to demonstrate the syntax. For giggles, I also added array of delegates, but it is not usually needed, because each delegate instance is mult-cast, so the other (better) way of doing is adding multiple method to the same delegate instance.
This technique is only useful if a delegate code and you calling class code are doing something together. For example, a delegate could operate on instance of the class passed as a parameter: you class calculates a parameter, you delegate instance add custom processing to it.
Very usual case is working on collection. For example, a calling class provides an iterator to work with some collection of objects, and the delegate instance performs some custom processing for each of the collection members, using
foreach
loop. For explanation of such techniques, please see my old Answer here:
What are the advantages of delegates in C#.NET?[
^].
—SA