I have a function:
private void SetupCallbacks()
{
Type actionType = Type.GetType(CardData.ActionFile);
if (actionType == null)
return;
// To get any particular method from actionType, I have to do the following
MethodInfo turnStarted = actionType.GetMethod(CardData.TurnStartedMethod);
if (turnStarted != null)
{
Delegate d = Delegate.CreateDelegate(typeof(Action<bool>), turnStarted);
Action<bool> turnStartedAction = (Action<bool>)d;
TurnManager.Instance.OnTurnStarted += turnStartedAction;
}
...
}
actionType
is a class that contains several static methods. These methods are stored as strings in the CardData object. I provided an example using the OnTurnStarted
callback. It is very clunky to write out all that code repeatedly each time I want to add another callback. I've tried creating a function:
private void SetupCallback<TDelegate>(Type actionType, string method, TDelegate delagateToAddThisTo) where TDelegate : Delegate
{
MethodInfo methodInfo = actionsContainerClass.GetMethod(method);
if (methodInfo != null)
{
Delegate d = Delegate.CreateDelegate(typeof(Action<Card>), methodInfo);
TDelegate t = (TDelegate)d;
delagateToAddThisTo += t;
}
}
However, where TDelegate : Delegate
doesn't work. I can't just do some type checking in the method (ie:
if(typeof(TDelegate).IsSubclassOf(typeof(Delegate)) == false)
{
throw new InvalidOperationException("Card::SetupCallback - " + typeof(TDelegate).Name + " is not a delegate");
}
because delagateToAddThisTo
which is of type TDelegate and needs to be able to be added to.
Thank you in advance.
C# doesn't allow constraining generic type parameters with delegate types. Your only option to validate delegate types is at runtime.
For the same reason, you won't be able to use the +=
operator inside the CreateCallback
method. But if +=
is moved to the caller (SetupCallbacks
), and the CreateCallback
only creates and returns the delegate, it can still look quite elegant:
// this code is in SetupCallbacks method
// Action<...> delegates are just examples
TurnManager.Instance.OnTurnStarted +=
CreateCallback<Action<string, int>>(actionType, CardData.TurnStartedMethod);
TurnManager.Instance.OnTurnStopped +=
CreateCallback<Action<string, int, TimeSpan>>(actionType, CardData.TurnStoppedMethod);
Where the CreateCallback
method is as follows:
private TDelegate CreateCallback<TDelegate>(Type actionType, string method)
where TDelegate : class
{
if (!typeof(Delegate).IsAssignableFrom(typeof(TDelegate)))
{
throw new InvalidOperationException("Card::SetupCallback - " + typeof(TDelegate).Name + " is not a delegate");
}
MethodInfo methodInfo = actionType.GetMethod(method);
if (methodInfo != null)
{
// the following line will also validate compatibility of delegate types
Delegate nonTypedDelegate = methodInfo.CreateDelegate(typeof(TDelegate));
TDelegate typedDelegate = (TDelegate)(object)nonTypedDelegate;
return typedDelegate;
}
return null;
}
Provided that in my example, TurnManager class looks like this:
public class TurnManager
{
public static TurnManager Instance
{
get { /* ....... */ }
}
public Action<string, int> OnTurnStarted { get; set; }
public Action<string, int, TimeSpan> OnTurnStopped { get; set; }
//... other members ...
}