调用泛型方法并使用运行时设置的类型传递泛型委托参数
Call generic method and pass generic delegate parameter using Type set at runtime
编辑:这是一个Unity 2018.4项目,它使用自定义版本的Mono。脚本运行时版本为“.NET 4.x 等效”,API 兼容级别为“.NET Standard 2.0”
我有一个使用泛型的自定义事件系统,其中 AddListener<T>
和 RemoveListener<T>
方法接受 EventDelegate<T>
参数。在代码的其他地方,给定一个在运行时确定的 Type
,我想使用适当类型的委托来添加和删除侦听器,并且只知道事件何时引发。我不需要知道事件附带的任何负载。
<T>
总是派生自特定的抽象基类型可能毫无价值。
在以下示例中,我在静态 class 上有一个静态泛型方法,该方法将泛型委托作为参数。我试图在运行时使用 Type
调用泛型方法,我想将 Type
的泛型委托作为参数传递。如果在编译时没有具有正确签名的已建立方法,这可能吗?
具有通用委托和接受委托作为参数的通用方法的静态 class:
public static class GameEvent
{
public delegate void EventDelegate<in T>(T e) where T : BaseEvent;
public static void AddListener<T>(EventDelegate<T> listener) where T : BaseEvent
{
...
}
public abstract class BaseEvent : EventArgs
{
}
}
Type
将始终派生自特定基类型。
派生的例子class:
public class CustomGameEvent : GameEvent.BaseEvent
{
public CustomGameEvent(object a, object b) // Constructor has 0 or more parameters of unknown type.
{
}
}
以上设置运行良好,我有许多正在运行的自定义事件。
例如,在某些class方法中,我会添加一个侦听器并实现一个用于接收事件的委托方法,
private void SomeMethod()
{
GameEvent.AddListener<CustomGameEvent>(this.CustomGameEventHandler);
}
private void CustomGameEventHandler(CustomGameEvent e)
{
... // This works and I can access properties of `e`.
}
现在进入我的问题。
在其他地方,我想使用运行时确定的 Type
调用 GameEvent.AddListner<T>
,其中 Type
派生自 GameEvent.BaseEvent
,但我不确定是什么在调用方法时作为参数传递。这就是我被困的地方。
我设置了一个接收 GameEvent.BaseEvent
参数而不是派生的 Type
的方法。当我希望委托方法接受派生类型时,使用接受基类型的方法是否有意义?
private Type type = typeof(CustomGameEvent); // Some type that inherits from BaseEvent.
private void GameEventHandler(GameEvent.BaseEvent e)
{
// Do something here. Don't care that e is the base type.
// I just want to know when the `Type` event was raised.
// Accept any parameter that is derived from GameEvent.BaseEvent.
}
private void AddListener()
{
var mi = typeof(GameEvent).GetMethod(nameof(GameEvent.AddListener));
// -> void AddListener[T](EventDelegate`1)
var miGeneric = mi?.MakeGenericMethod(type);
// -> void AddListener[CustomGameEvent](EventDelegate`1)
var delegateType = typeof(GameEvent.EventDelegate<>).MakeGenericType(type);
// -> GameEvent+EventDelegate`1[CustomGameEvent]
var parameters = new object[] { (GameEvent.EventDelegate<GameEvent.BaseEvent>)this.GameEventHandler };
miGeneric?.Invoke(null, parameters);
// -> When "AddListener<CustomGameEvent>()" is invoked, it receives a
// "GameEvent+EventDelegate`1[GameEvent+BaseEvent]" not a
// "GameEvent+EventDelegate`1[CustomGameEvent]"
}
这在大多数情况下都有效,并且符合我的预期,因为我明确地将 this.GameEventHandler
转换为 (GameEvent.EventDelegate<GameEvent.BaseEvent>)
并将其作为参数传递。但我想将参数对象转换为 delegateType
中存储的类型,以便 AddListener<CustomGameEvent>()
实际上接收到 EventDelegate<CustomGameEvent>
而不是 EventDelegate<GameEvent.BaseEvent>
。这可能吗?如果可能,怎么做?
如果对象被强制转换为 EventDelegate<CustomGameEvent>
,甚至可以将 GameEventHandler(GameEvent.BaseEvent e)
用作委托方法,或者调用是否会由于签名不匹配而失败?
我试过使用 Convert.ChangeType()
,但是 InvalidCastException: Object must implement IConvertible.
失败了。
var parameters = new object[]
{
Convert.ChangeType(
(GameEvent.EventDelegate<GameEvent.BaseEvent>)this.GameEventHandler,
delegateType)
};
我也尝试过使用通用的静态辅助方法 CastInto<T>(object obj)
,但它并没有像我希望的那样进行转换。
private void AddListener()
{
... // delegateType is stored
var miCastInto = this.GetType().GetMethod(nameof(CastInto)).MakeGenericMethod(delegateType);
// -> EventDelegate`1 CastInto[EventDelegate`1](System.Object), but is that what
// I want? Should it be: EventDelegate`1[CustomGameEvent]
// CastInto[EventDelegate`1[CustomGameEvent]](System.Object)
object o1 = (GameEvent.EventDelegate<GameEvent.BaseEvent>)this.GameEventHandler;
// -> GameEvent.EventDelegate<GameEvent.BaseEvent> ... as expected.
object o2 = miCastInto.Invoke(null, new[] { o });
// -> GameEvent.EventDelegate<GameEvent.BaseEvent> ... not <CustomGameEvent>.
var parameters = new object[] { castedObject }; // <- Wrong parameter type.
...
}
public static T CastInto<T>(object obj)
{
return (T)obj;
}
我什至不确定我是否应该尝试巧妙地将 GameEventHandler(GameEvent.BaseEvent e)
委托给 typeDelegate
。如果可以转换,那么在引发事件时 GameEventHandler(GameEvent.BaseEvent e)
是否仍会被调用?
我看到了Delegate.CreateDelegate()
,但是我真的不明白是否应该在这里使用它或者应该如何使用它。
无论建立什么委托,它也将在以后与类似的 GameEvent.RemoveListener<T>
调用一起使用(使用相同的 Type
),因此委托可能必须与 class 实例,以后使用时不会重新创建。我假设第二个委托创建将被视为不同的(与原始创建的引用不同)。
您可以使用 Delegate.CreateDelegate
创建特定 Type
对象的委托。
var delegateType = typeof(GameEvent.EventDelegate<>).MakeGenericType(type);
// -> GameEvent+EventDelegate`1[CustomGameEvent]
var methodInfo = typeof(YourClass).GetMethod(
"GameEventHandler", BindingFlags.NonPublic | BindingFlags.Instance);
var parameters = new object[] {
Delegate.CreateDelegate(delegateType, this, methodInfo)
};
您在此处调用的 overload 创建了一个调用实例方法的委托。第二个参数表示您希望调用该方法的实例。
编辑:这是一个Unity 2018.4项目,它使用自定义版本的Mono。脚本运行时版本为“.NET 4.x 等效”,API 兼容级别为“.NET Standard 2.0”
我有一个使用泛型的自定义事件系统,其中 AddListener<T>
和 RemoveListener<T>
方法接受 EventDelegate<T>
参数。在代码的其他地方,给定一个在运行时确定的 Type
,我想使用适当类型的委托来添加和删除侦听器,并且只知道事件何时引发。我不需要知道事件附带的任何负载。
<T>
总是派生自特定的抽象基类型可能毫无价值。
在以下示例中,我在静态 class 上有一个静态泛型方法,该方法将泛型委托作为参数。我试图在运行时使用 Type
调用泛型方法,我想将 Type
的泛型委托作为参数传递。如果在编译时没有具有正确签名的已建立方法,这可能吗?
具有通用委托和接受委托作为参数的通用方法的静态 class:
public static class GameEvent
{
public delegate void EventDelegate<in T>(T e) where T : BaseEvent;
public static void AddListener<T>(EventDelegate<T> listener) where T : BaseEvent
{
...
}
public abstract class BaseEvent : EventArgs
{
}
}
Type
将始终派生自特定基类型。
派生的例子class:
public class CustomGameEvent : GameEvent.BaseEvent
{
public CustomGameEvent(object a, object b) // Constructor has 0 or more parameters of unknown type.
{
}
}
以上设置运行良好,我有许多正在运行的自定义事件。
例如,在某些class方法中,我会添加一个侦听器并实现一个用于接收事件的委托方法,
private void SomeMethod()
{
GameEvent.AddListener<CustomGameEvent>(this.CustomGameEventHandler);
}
private void CustomGameEventHandler(CustomGameEvent e)
{
... // This works and I can access properties of `e`.
}
现在进入我的问题。
在其他地方,我想使用运行时确定的 Type
调用 GameEvent.AddListner<T>
,其中 Type
派生自 GameEvent.BaseEvent
,但我不确定是什么在调用方法时作为参数传递。这就是我被困的地方。
我设置了一个接收 GameEvent.BaseEvent
参数而不是派生的 Type
的方法。当我希望委托方法接受派生类型时,使用接受基类型的方法是否有意义?
private Type type = typeof(CustomGameEvent); // Some type that inherits from BaseEvent.
private void GameEventHandler(GameEvent.BaseEvent e)
{
// Do something here. Don't care that e is the base type.
// I just want to know when the `Type` event was raised.
// Accept any parameter that is derived from GameEvent.BaseEvent.
}
private void AddListener()
{
var mi = typeof(GameEvent).GetMethod(nameof(GameEvent.AddListener));
// -> void AddListener[T](EventDelegate`1)
var miGeneric = mi?.MakeGenericMethod(type);
// -> void AddListener[CustomGameEvent](EventDelegate`1)
var delegateType = typeof(GameEvent.EventDelegate<>).MakeGenericType(type);
// -> GameEvent+EventDelegate`1[CustomGameEvent]
var parameters = new object[] { (GameEvent.EventDelegate<GameEvent.BaseEvent>)this.GameEventHandler };
miGeneric?.Invoke(null, parameters);
// -> When "AddListener<CustomGameEvent>()" is invoked, it receives a
// "GameEvent+EventDelegate`1[GameEvent+BaseEvent]" not a
// "GameEvent+EventDelegate`1[CustomGameEvent]"
}
这在大多数情况下都有效,并且符合我的预期,因为我明确地将 this.GameEventHandler
转换为 (GameEvent.EventDelegate<GameEvent.BaseEvent>)
并将其作为参数传递。但我想将参数对象转换为 delegateType
中存储的类型,以便 AddListener<CustomGameEvent>()
实际上接收到 EventDelegate<CustomGameEvent>
而不是 EventDelegate<GameEvent.BaseEvent>
。这可能吗?如果可能,怎么做?
如果对象被强制转换为 EventDelegate<CustomGameEvent>
,甚至可以将 GameEventHandler(GameEvent.BaseEvent e)
用作委托方法,或者调用是否会由于签名不匹配而失败?
我试过使用 Convert.ChangeType()
,但是 InvalidCastException: Object must implement IConvertible.
失败了。
var parameters = new object[]
{
Convert.ChangeType(
(GameEvent.EventDelegate<GameEvent.BaseEvent>)this.GameEventHandler,
delegateType)
};
我也尝试过使用通用的静态辅助方法 CastInto<T>(object obj)
,但它并没有像我希望的那样进行转换。
private void AddListener()
{
... // delegateType is stored
var miCastInto = this.GetType().GetMethod(nameof(CastInto)).MakeGenericMethod(delegateType);
// -> EventDelegate`1 CastInto[EventDelegate`1](System.Object), but is that what
// I want? Should it be: EventDelegate`1[CustomGameEvent]
// CastInto[EventDelegate`1[CustomGameEvent]](System.Object)
object o1 = (GameEvent.EventDelegate<GameEvent.BaseEvent>)this.GameEventHandler;
// -> GameEvent.EventDelegate<GameEvent.BaseEvent> ... as expected.
object o2 = miCastInto.Invoke(null, new[] { o });
// -> GameEvent.EventDelegate<GameEvent.BaseEvent> ... not <CustomGameEvent>.
var parameters = new object[] { castedObject }; // <- Wrong parameter type.
...
}
public static T CastInto<T>(object obj)
{
return (T)obj;
}
我什至不确定我是否应该尝试巧妙地将 GameEventHandler(GameEvent.BaseEvent e)
委托给 typeDelegate
。如果可以转换,那么在引发事件时 GameEventHandler(GameEvent.BaseEvent e)
是否仍会被调用?
我看到了Delegate.CreateDelegate()
,但是我真的不明白是否应该在这里使用它或者应该如何使用它。
无论建立什么委托,它也将在以后与类似的 GameEvent.RemoveListener<T>
调用一起使用(使用相同的 Type
),因此委托可能必须与 class 实例,以后使用时不会重新创建。我假设第二个委托创建将被视为不同的(与原始创建的引用不同)。
您可以使用 Delegate.CreateDelegate
创建特定 Type
对象的委托。
var delegateType = typeof(GameEvent.EventDelegate<>).MakeGenericType(type);
// -> GameEvent+EventDelegate`1[CustomGameEvent]
var methodInfo = typeof(YourClass).GetMethod(
"GameEventHandler", BindingFlags.NonPublic | BindingFlags.Instance);
var parameters = new object[] {
Delegate.CreateDelegate(delegateType, this, methodInfo)
};
您在此处调用的 overload 创建了一个调用实例方法的委托。第二个参数表示您希望调用该方法的实例。