繁体   English   中英

如何将通用集合转换为祖先的通用集合?

[英]How to cast a generic collection to an generic collection of ancestors?

考虑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)
{
}

但这意味着必须改写所有使用泛型的代码。

你不能投; 您必须自己进行转换。

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

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

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

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

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

List<Control>具有一个采用IEnumerable<Control>的构造函数。 为什么我们可以传递labels 由于IEnumerable<T>是协变的:您不能将任何内容放入IEnumerable<T> 你只能拿东西。 因此,您知道可以将从IEnumerable<T>检索到的任何内容都视为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);
}

这个问题的快速答案:

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中问世以来,我们就普遍反对转换,认为它在某种程度上是“不好的”,但是有时候在处理多态性时,转换只是必要的。

我找到的解决方案-停止使用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问题就可以到达那里。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM