让我们说我想将代表存储在这样的集合中:
public void AddDelegate<T>(Action<T> action) where T : ISomething
{
_delegates.Add(action);
}
_delegates
的类型应该是什么?
尝试使用IList<Action<ISomething>> _delegates;
导致上述Argument type 'System.Action<T>' is not assignable to parameter type 'System.Action<ISomething>'
调用的错误消息 Add
。但为什么它不起作用?编译器应该知道&#39; T必须是ISomething
。
如果我不想通过使用例如以下方式来放松类型安全,我有什么选择? IList<object>
或参数化泛型类型T
上的整个类?
答案 0 :(得分:4)
_delegates
必须是IList<Action<T>>
类型。
因此,您必须将<T> where T : ISomething
添加到班级。
或者,直接取消泛型并支持Action<ISomething>
。
所以你的两个选择是:
public class Delegates<T> where T : ISomething
{
private List<Action<T>> _delegates;
public void AddDelegate(Action<T> action)
{
_delegates.Add(action);
}
}
或者
public class Delegates
{
private List<Action<ISomething>> _delegates;
public void AddDelegate(Action<ISomething> action)
{
_delegates.Add(action);
}
}
修改强>
As Sehnsucht points out, there's a third option:将Action<T>
代理包含在Action<ISomething>
代理中,然后OP可以实现他原来想要的内容。
原因是虽然T
是ISomething
的“子类型”(实施者),但Action<T>
不是Action<ISomething>
的子类型,实际上Action<T>
3}},相反的情况实际上是正确的(尽管看起来反直觉)。即使使用强制转换,也无法将Action<ISomething>
的实例添加到{{1}}列表中。
答案 1 :(得分:2)
但为什么不起作用?编译器应该“知道”T必须是ISomething。
Action<in T>
在T
中是逆变的,这意味着Action<Derived>
不是Action<Base>
的子类型,即使Derived
是Base
的子类型}。
事实上,逆变意味着相反:Action<Derived>
是Action<Base>
的超类型。也就是说,可以将Action<object>
分配给Action<string>
。
在您的情况下,Action<T> where T : ISomething
无法分配给Action<ISomething>
,但可能相反。
答案 2 :(得分:2)
您应该看到here以更深入地了解问题
我可以提出&#34;那种黑客攻击&#34;为了避免这个问题,我不能说它是好事还是真的只是一个黑客。
void Add<T> (Action<T> action) where T : ISomething
{
Action<ISomething> typedAction = something => action ((T) something);
_delegates.Add (typedAction);
}
答案 3 :(得分:1)
您的示例无法正常工作,因为虽然编译器知道T
为ISomething
,但它并不知道 ISomething
的哪个子类型提供。如果提供的类型始终是参数类型的子类型
这是不安全的,例如:
class FirstSomething : ISomething { }
class SecondSomething : ISomething { }
Action<FirstSomething> act = f => { }
Action<ISomething> sa = act; //not allowed
sa(new SecondSomething()); //unsafe!
Action<T>
在T
中是逆变的,这意味着如果Action<U>
是U
的子类型,它只能被分配给T
,它不是在您的情况下,因为ISomething
是T
的超类型。