Reputation: 53
I'm having trouble preventing duplicate code.
Currently I have these methods:
protected delegate bool CallbackDelegate<T>(T param, out string result);
protected delegate bool CallbackDelegate<T, U>(T param1, U param2, out string result);
protected bool ClientCtrlCallback<T>(T param, CallbackDelegate<T> callbackMethod)
{...}
protected bool ClientCtrlCallback<T,U>(T param1, U param2, CallbackDelegate<T,U> callbackMethod)
{...}
Both ClientCtrlCallback methods have equal code (checking validity of communication channels, try-catch block, obtaining lock, etc) built around the call to the CallbackDelegate method.
I've been trying to merge these ClientCtrl methods, but didn't succeed because the delegate constraint is not allowed. This is as far as I got: (unnecessary code removed for clarity)
protected delegate bool CallbackDelegate1<T>(T param, out string result);
protected delegate bool CallbackDelegate2<T, U>(T param1, U param2, out string result);
protected const string METHOD_MY_DELEGATE1 = "CallbackDelegate1";
protected const string METHOD_MY_DELEGATE2 = "CallbackDelegate2";
protected interface ParameterSet { };
protected class OneParameter<T>: ParameterSet { public T p1; };
protected class TwoParameters<T,U> : ParameterSet { public T p1; public U p2; };
protected bool ClientCtrlCallback<D,T,U>(ParameterSet parameterset, D callbackMethod, string successLog = null) // where MyDelegate : delegate //not allowed
{
// delegate constrained is not allowed, so check it here
if (!typeof(D).IsSubclassOf(typeof(Delegate)))
return false;
// check name of method (this works)
string methodName = (callbackMethod as Delegate).Method.Name;
// Call the delegate // doesn't work.
string result;
switch (methodName)
{
case METHOD_MY_DELEGATE1:
// doesnt work
//(callbackMethod as Delegate)((parameterset as OneParameter<T>).p1, out result);
break;
case METHOD_MY_DELEGATE2:
// doesnt work
//(callbackMethod as Delegate)((parameterset as TwoParameters<T, U>).p1, (parameterset as TwoParameters<T, U>).p2, out result); // doesnt work
break;
}
return true;
}
It starts getting ugly with the parameter classes. It gets worse with the switch comparing the delegates' names (I also don't like having to each give them a different name). And then there's total trouble when I want to call the Delegate's method: compile time error Method name expected
I don't understand why I can get the method name from the Delegate, but not invoke the Delegate's method. Am I missing something big?
Upvotes: 0
Views: 250
Reputation: 1561
EDITED
Can't you just have:
protected delegate bool CallbackDelegate<TParameterSet>(TParameterSet param, out string result)
where TParameterSet : ParameterSet;
Then (doesn't seem that useful now, depending on the rest of your code):
protected bool ClientCtrlCallback<TParameterSet>(TParameterSet parameterset, CallbackDelegate<TParameterSet> callbackMethod, string successLog = null)
where TParameterSet : ParameterSet
{
string result;
return callbackMethod(parameterset, out result);
}
And finally your callback methods:
bool CallbackMethodOneParameter<T>(OneParameter<T> parameter, out string result)
{
// Do your stuff with parameter.p1;
// set result
// return success or fail
}
bool CallbackMethodTwoParameters<T, U>(TwoParameters<T, U> parameters, out string result)
{
// Do your stuff with parameters.p1 and parameters.p2;
// set result
// return success or fail
}
NO LONGER APPLY
I'm also wondering if you really need to use generics here. Your callback methods might already know which types your ParameterSet contains.
Because, with this generics, if you need another argument, you will have to change everywhere this signature. And it can become painful.
In your code, to call your delegate, you can do that:
var parameters = new object[] { (parameterset as OneParameter<T>).p1, null };
var success = (callbackMethod as Delegate).DynamicInvoke(parameters);
result = parameters[1];
Upvotes: 0
Reputation: 203826
Don't make it the responsibility of this method to accept parameters that will be passed into the callback in the first place. Don't pass in any parameters. If the caller has a value that they want to use within the callback then can use a closure to close over that variable.
Not only is it extremely hard for this object to accept an arbitrary number of parameters and pass them into the callback (and impossible to fully generalize in a type safe manner) and also trivially easy for the caller to solve this problem in an entirely general case and fully type safe manner, but that information is logically a private implementation detail of the caller, and isn't information that this method has any reason to know about in the first place.
Upvotes: 1