C#将类型委托为通用约束

时间:2017-07-05 17:09:23

标签: c# generics unity3d delegates action

我有一个功能:

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是一个包含多个静态方法的类。这些方法作为字符串存储在CardData对象中。我使用OnTurnStarted回调提供了一个示例。每次我想添加另一个回调时,重复写出所有代码是非常笨重的。我尝试过创建一个函数:

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;
        }
}

但是,where TDelegate : Delegate不起作用。我不能在方法中做一些类型检查(即:

if(typeof(TDelegate).IsSubclassOf(typeof(Delegate)) == false)
{
  throw new InvalidOperationException("Card::SetupCallback - " + typeof(TDelegate).Name + " is not a delegate");
}

因为delagateToAddThisTo属于TDelegate类型并且需要能够添加到其中。

提前谢谢你。

2 个答案:

答案 0 :(得分:3)

C#不允许使用委托类型约束泛型类型参数。验证委托类型的唯一选择是在运行时。

出于同样的原因,您将无法在+=方法中使用CreateCallback运算符。但是如果将+=移动到调用者(SetupCallbacks),并且CreateCallback仅创建并返回委托,它仍然看起来非常优雅:

// 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);

CreateCallback方法如下:

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;
}

前提是在我的例子中,TurnManager类看起来像这样:

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 ...
}

答案 1 :(得分:0)

从 C# 7.3 开始是可能的。

来自 Microsoft 文档的示例

public class UsingEnum<T> where T : System.Enum { }

public class UsingDelegate<T> where T : System.Delegate { }

public class Multicaster<T> where T : System.MulticastDelegate { }

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/where-generic-type-constraint