[英]Why is the generic parameter not casting?
我遺漏了一些基本的東西,但我無法理解。 鑒於:
abstract class EventBase {}
class SpecialEvent : EventBase {}
在另一個類中我希望允許調用者能夠RegisterFor<SpecialEvent>(x => {...})
public class FooHandler {
{
internal Dictionary<Type, Action<EventBase>> _validTypes = new Dictionary<Type, Action<EventBase>>();
internal void RegisterFor<T>(Action<T> handlerFcn) where T: EventBase
{
_validTypes.Add(typeof(T), handlerFcn);
}
}
但是, _validTypes.Add
行無法編譯。 它無法將Action<T>
轉換為Action<EventBase>
。 約束指定T
必須從EventBase
派生,所以我誤解了什么?
C#是不正確的。 要了解原因,請考慮以下情況:
// This is your delegate implementation
void SpecialAction(SpecialEvent e) {
Console.WriteLine("I'm so special!");
}
// This is another EventBase class
class NotSoSpecialEvent : EventBase {}
void PureEvil(Action<EventBase> handlerFcn) where T: EventBase {
handlerFcn(new NotSoSpecialEvent()); // Why not?
}
讓我們假設C#允許您為Action<SpecialEvent>
傳遞Action<EventBase>
。 接下來會發生什么:
PureEvil(SpecialAction); // Not allowed
現在, PureEvil
將嘗試傳遞NotSoSpecialEvent
以委托使用SpecialAction
SpecialEvent
,而這必須永遠不會發生。
動作委托是逆變的 - 類型被定義為Action<in T>
。 這對於替代原則在應用於行動時如何運作具有重要意義。
考慮兩種類型: B
(基礎)和D
(派生)。 然后Action<B>
比Action<D>
派生更多 。 這意味着以下行為:
class B { }
class D : B { }
...
Action<D> derivedAction = ...
Action<B> baseAction = derivedAction; // Fails
Action<D> derivedAction1 = baseAction; // Succeeds
在您的示例中,如果SpecialEvent
是從EventBase
派生的類型,則只允許分配Action<SpecialEvent> = Action<EventBase>
,而不是相反(當您嘗試時)。
由於在將代理存儲在字典中之前已經檢查了事件類型,因此您不必堅持存儲強類型代理 - 更不用說由於Action
代理的逆轉而不能堅持強類型。
您可以在字典中存儲任何您喜歡的內容,例如Delegate
或plain object
,然后在從集合中獲取Action
委托時,向下轉換為具體的Action<T>
。
public class FooHandler
{
internal Dictionary<Type, object> _validTypes =
new Dictionary<Type, object>();
internal void RegisterFor<T>(Action<T> handlerFcn)
where T: EventBase
{
_validTypes.Add(typeof(T), handlerFcn);
}
internal Action<T> Find<T>()
where T : EventBase =>
(Action<T>)_validTypes[typeof(T)];
}
Action<SpecialEvent>
不能用作Action<EventBase>
。
使用Delegate
抽象參數,然后將其強制轉換:
public class FooHandler
{
internal Dictionary<Type, Delegate> _validTypes = new Dictionary<Type, Delegate>();
internal void RegisterFor<T>(Action<T> handlerFcn) where T : EventBase
{
_validTypes.Add(typeof(T), handlerFcn);
}
internal void ExecuteHandler<T>(T value) where T : EventBase
{
var handler = (Action<T>)_validTypes[typeof(T)];
handler(value);
}
}
像這樣使用它:
var handler = new Action<SpecialEvent>(ev => { Console.WriteLine("works!"); });
FooHandler.RegisterFor<SpecialEvent>(handler);
FooHandler.ExecuteHandler(new SpecialEvent());
你有一個Action<SpecialEvent>
,只知道處理SpecialEvent
。 Action<EventBase>
表示可以將任何 EventBase
傳遞給它。 這使得轉換不安全。
在您的情況下,我會選擇Dictionary<Type, Delegate>
,其中每個T
鍵與Action<T>
值配對。 如果您可以確保只添加正確類型的值,則可以在要調用它時將代理安全地轉換回Action<T>
。
如果Dictionary
中的一些Action<EventBase>
是Action<UnexpectedEvent>
而不是Action<SpecialEvent>
怎么辦? Action<T>
是逆變的,但它不是協變的。
請參閱: https : //blogs.msdn.microsoft.com/ericlippert/2007/10/16/covariance-and-contravariance-in-c-part-one/
如果您碰巧知道類型應該解決,和/或在類型沖突時想要一個異常,您可以將它包裝在另一個執行強制轉換的操作中,如下所示:
_validTypes.Add(typeof(T), eventBase => handlerFcn((T)eventBase));
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.