如何将泛型集合强制转换为祖先的泛型集合?

Ian*_*oyd 1 c# generics

考虑以下列表Label:

Collection<Label> labels = new Collection<Label>();
Run Code Online (Sandbox Code Playgroud)

现在我想将其转换为以下集合Controls:

public void ScaleControls(ICollection<Control> controls) {}
Run Code Online (Sandbox Code Playgroud)

我会试着打电话:

ScaleControls(labels);
Run Code Online (Sandbox Code Playgroud)

但那不会编译.

ScaleControls((ICollection<Control>)labels);
Run Code Online (Sandbox Code Playgroud)

编译,但在运行时崩溃.

ICollection<Control> controls = (ICollection<Control>)labels;
ScaleControls(c);
Run Code Online (Sandbox Code Playgroud)

编译,但在运行时崩溃.

有没有办法传递对象的通用列表?


另一种方法是放弃通用列表,并使用类型化列表:

public class ControlList : Collection<Control>
{
}

pubic void InvalidateControls(ControlList controls)
{
}
Run Code Online (Sandbox Code Playgroud)

但这意味着所有使用泛型的代码都必须进行改造.

pho*_*oog 10

你不能施展它; 你必须自己转换它.

InvalidateControls(new List<Control>(labels));  //C# 4 or later
Run Code Online (Sandbox Code Playgroud)

你在这里的问题ICollection<T>是不协变.ICollection不协变的原因是为了防止这样的方法变得邪恶:

void AddControl(ICollection<Control> controls, Control control)
{
    controls.Add(control);
}
Run Code Online (Sandbox Code Playgroud)

为什么那会是邪恶的?因为如果ICollection是协变的,该方法将允许您将TextBox添加到标签列表:

AddControl(new List<Label>(), new TextBox());
Run Code Online (Sandbox Code Playgroud)

List<Control>有一个构造函数IEnumerable<Control>.为什么我们可以通过labels?因为IEnumerable<T>是协变的:你不能把任何东西放进去IEnumerable<T>; 你只能拿出来.因此,您知道您可以处理从IEnumerable<T>T(当然)或其任何基本类型中检索到的任何内容.

编辑

我刚注意到你正在使用.Net 3.5.在这种情况下,IEnumerable<T>不是协变的,你需要更多的代码来转换集合.像这样的东西:

ICollection<T> ConvertCollection<T, U>(ICollection<U> collection) where U : T
{
    var result = new List<T>(collection.Count);
    foreach (var item in collection)
        result.Add(item);
}
Run Code Online (Sandbox Code Playgroud)