如何在C#中的类型安全集合中存储具有约束泛型类型的Action委托?

时间:2015-05-29 12:43:29

标签: c# generics delegates

让我们说我想将代表存储在这样的集合中:

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上的整个类?

4 个答案:

答案 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可以实现他原来想要的内容。

原因是虽然TISomething的“子类型”(实施者),但Action<T>不是Action<ISomething>的子类型,实际上Action<T> 3}},相反的情况实际上是正确的(尽管看起来反直觉)。即使使用强制转换,也无法将Action<ISomething>的实例添加到{{1}}列表中。

答案 1 :(得分:2)

  

但为什么不起作用?编译器应该“知道”T必须是ISomething。

Action<in T>T中是逆变的,这意味着Action<Derived>不是Action<Base>的子类型,即使DerivedBase的子类型}。

事实上,逆变意味着相反: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)

您的示例无法正常工作,因为虽然编译器知道TISomething,但它并不知道 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,它不是在您的情况下,因为ISomethingT的超类型。