[英]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.