如何在C#中的类型安全集合中存储具有约束generics类型的Action委托?
假设我想将代理存储在这样的集合中:
public void AddDelegate(Action action) where T : ISomething { _delegates.Add(action); }
_delegates
的类型应该是什么?
尝试使用IList<Action> _delegates;
导致错误消息Argument type 'System.Action' is not assignable to parameter type 'System.Action'
上面的Add
调用的Argument type 'System.Action' is not assignable to parameter type 'System.Action'
。 但为什么不起作用? 编译器应该“知道”T必须是ISomething
。 如果我不想通过使用例如IList
或参数化generics类型T
上的整个类来放弃类型安全,我有哪些选择?
_delegates
必须是IList
。
因此,你必须在课堂上添加
。
或者,取消generics并直接支持Action
。
所以你的两个选择是:
public class Delegates where T : ISomething { private List> _delegates; public void AddDelegate(Action action) { _delegates.Add(action); } }
要么
public class Delegates { private List> _delegates; public void AddDelegate(Action action) { _delegates.Add(action); } }
编辑 如Sehnsucht所指出的,还有第三种选择 :将Action
委托包装在Action
委托中,然后OP可以实现他最初想要的东西。
原因是虽然T
是ISomething
的“子类型”(实现者),但Action
不是Action
的子类型,事实上正如dcastro在他的回答中所解释的那样 ,事实恰恰相反(尽管如此)看似违反直觉)。 即使使用强制转换,也无法将Action
的实例添加到Action
列表中。
但为什么不起作用? 编译器应该“知道”T必须是ISomething。
Action
是逆变的,这意味着Action
不是Action
的子类型,即使Derived
是Base
的子类型。
实际上,逆向意味着相反: Action
是Action
的超类型 。 也就是说,可以将Action
分配给Action
。
在您的情况下, Action
无法分配给Action
,但相反的情况是可能的。
你应该在这里看到更深入的问题
我可以提出“一种黑客”来规避这个问题,但我不能说这是好事还是真的只是一个黑客。
void Add (Action action) where T : ISomething { Action typedAction = something => action ((T) something); _delegates.Add (typedAction); }
您的示例将无法工作,因为虽然编译器知道T
是ISomething
但它不知道将提供哪种 ISomething
子类型。 如果提供的类型始终是参数类型的子类型,则只能安全地完成
例如,这将是不安全的:
class FirstSomething : ISomething { } class SecondSomething : ISomething { } Action act = f => { } Action sa = act; //not allowed sa(new SecondSomething()); //unsafe!
Action
在T
中是逆变的,这意味着如果U
是T
的子类型,它只能被赋值给Action
,因为ISomething
是T
的超类型,所以它不在你的情况下。