考虑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)
{
}
但这意味着所有使用泛型的代码都必须进行改造。
答案 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问题来实现目标。