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

时间:2012-01-10 20:53:19

标签: c# generics

考虑Label的列表:

Collection<Label> labels = new Collection<Label>();

现在我想将其转换为Controls

的集合
public void ScaleControls(ICollection<Control> controls) {}

我会试着打电话:

ScaleControls(labels);

但这不会编译。

ScaleControls((ICollection<Control>)labels);

编译,但在运行时崩溃。

ICollection<Control> controls = (ICollection<Control>)labels;
ScaleControls(c);

编译,但在运行时崩溃。

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


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

public class ControlList : Collection<Control>
{
}

pubic void InvalidateControls(ControlList controls)
{
}

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

3 个答案:

答案 0 :(得分:10)

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

InvalidateControls(new List<Control>(labels));  //C# 4 or later

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

void AddControl(ICollection<Control> controls, Control control)
{
    controls.Add(control);
}

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

AddControl(new List<Label>(), new TextBox());

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);
}

答案 1 :(得分:3)

快速回答问题:

labels.Cast<Control>().ToList()

这将创建一个全新的单独列表,因此如果您要将新集合传递给向其添加控件的方法,则该新控件将不会反映在原始集合中labels

另一种方法是查看要将集合传递给的方法。假设您有一个如下方法:

    void AddControl(List<Control> controls, string controlName)
    {
        Control ctrl = this.FindControlByName(controlName);

        controls.Add(ctrl);
    }

您不能将List<Label>对象传递给此方法,但如果您将其重写为通用方法,则可以如下所示:

    void AddControl<T>(List<T> controls, string controlName)
        where T : Control
    {
        Control ctrl = this.FindControlByName(controlName);

        controls.Add((T)ctrl); // a cast is required
    }

当然,根据您的具体情况,上述建议可能无法或不可取。

如您自己的回答所示,另一种可能性是利用非通用接口。这是一种非常有效的方法;我认为,自从.NET 2.0中的泛型出现以来,我们通常会厌恶转换,认为它在某种程度上“糟糕”,但有时在处理多态时只需要转换。

答案 2 :(得分:0)

我找到的解决方案 - 停止使用Collections

List<Label> labels = new List<Label>();

然后方法变为:

public void ScaleControls(IList controls) {}

我得到了通用列表的好处,没有.NET抱怨它不能做到显而易见。

如果有人打来电话:

controls.Add(new System.Xml.XmlDocument());

然后我收到错误:

  

<强>的ArgumentException
  值“System.Xml.XmlDocument”不是“System.Windows.Forms.Label”类型,不能在此通用集合中使用。

正如我所料。

我只花了四天时间和七个stackoverflow问题来实现目标。