編輯:這是一個 Unity 2018.4 專案,它使用 Mono 的自定義版本。腳本運行時版本為“.NET 4.x Equivalent”,API 兼容級別為“.NET Standard 2.0”
我有一個使用泛型AddListener<T>和RemoveListener<T>接受EventDelegate<T>引數的方法的自定義事件系統。在代碼的其他地方,如果Type在運行時確定,我想使用適當型別的委托添加和洗掉偵聽器,并且只知道何時引發事件。我不需要知道事件附帶的任何有效負載。
<T>總是從特定的抽象基型別派生的可能毫無價值。
在下面的示例中,我在靜態類上有一個靜態泛型方法,該方法將泛型委托作為引數。我試圖Type在運行時呼叫泛型方法,我想將泛型委托Type作為引數傳遞。如果在編譯時沒有具有正確簽名的既定方法,這可能嗎?
具有泛型委托和接受委托作為引數的泛型方法的靜態類:
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 將始終派生自特定的基型別。
派生類示例:
public class CustomGameEvent : GameEvent.BaseEvent
{
public CustomGameEvent(object a, object b) // Constructor has 0 or more parameters of unknown type.
{
}
}
上述設定運行良好,我有許多正在運行的自定義事件。
例如,在某些類方法中,我會添加一個偵聽器并實作一個用于接收事件的委托方法,
private void SomeMethod()
{
GameEvent.AddListener<CustomGameEvent>(this.CustomGameEventHandler);
}
private void CustomGameEventHandler(CustomGameEvent e)
{
... // This works and I can access properties of `e`.
}
現在談談我的問題。
Elsewhere, I would like to call GameEvent.AddListner<T> using a Type determined at runtime, where Type is derived from GameEvent.BaseEvent, however I'm not sure what to pass as a parameter when invoking the method. That's where I'm stuck.
I setup a method that receives a GameEvent.BaseEvent parameter instead of the derived Type. Does it even make sense to have a method that accepts the base type when I'd like the delegate method to accept the derived 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 works for the most part and does what I'd expect, because I'm explicitly casting the this.GameEventHandler to (GameEvent.EventDelegate<GameEvent.BaseEvent>) and passing that as a parameter. But I'd like to cast the parameter object to the type stored in delegateType, so that AddListener<CustomGameEvent>() actually receives an EventDelegate<CustomGameEvent> and not a EventDelegate<GameEvent.BaseEvent>. Is that possible, and if so, how?
Can GameEventHandler(GameEvent.BaseEvent e) even be used as the delegate method if the object was cast to EventDelegate<CustomGameEvent> or would the invoke fail due to the signature mismatch?
I've tried using Convert.ChangeType(), however that fails with InvalidCastException: Object must implement IConvertible..
var parameters = new object[]
{
Convert.ChangeType(
(GameEvent.EventDelegate<GameEvent.BaseEvent>)this.GameEventHandler,
delegateType)
};
I also tried using a generic static helper method CastInto<T>(object obj), but that didn't cast as I hoped it would.
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;
}
I'm not even sure if I should be trying to finesse the GameEventHandler(GameEvent.BaseEvent e) delegate to typeDelegate. If it can be converted would GameEventHandler(GameEvent.BaseEvent e) still be called when the event was raised?
I saw Delegate.CreateDelegate(), but I don't really understand if that should be used here or how it should be used.
無論建立什么委托,它也將GameEvent.RemoveListener<T>在以后的類似呼叫中使用(使用相同的Type),因此委托可能必須與類實體一起存盤,并且在以后使用時不會重新創建。我假設第二個委托創建將被視為不同的(與原始創建的參考不同)。
uj5u.com熱心網友回復:
您可以使用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)
};
您在此處呼叫的多載會創建一個呼叫實體方法的委托。第二個引數指示您希望呼叫該方法的實體。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/311155.html
上一篇:轉到通用頻道型別
下一篇:T型如何使用set()方法
