[英]C# Generics - Lists of collections
我有一个基于抽象Device
类的类层次结构。 所有设备的存储库看起来都像这样:
class Hardware
{
public readonly DeviceCollection<Switch> Switches = ...;
public readonly DeviceCollection<Light> Lights = ...;
}
其中DeviceCollection
实现IEnumerable<T> where T : Device
。
我需要枚举所有设备,而我目前的糟糕代码就是这样做的
protected override IEnumerator<Device> enumerate()
{
foreach (var light in Lights)
{
yield return light;
}
foreach (var @switch in Switches)
{
yield return @switch;
}
}
这并不健壮,因为有时我会添加一些新的硬件,一个新的DeviceCollection
并且很容易忘记在上面添加一个新的迭代。 所以我想一点反思会有所帮助 - 懒洋洋地构建一个DeviceCollection
字段列表并运行它。 但该清单的声明是什么样的呢?
private List<DeviceCollection<T>> _collections;
不编译。 也没有
private List<DeviceCollection> _collections;
我如何声明此列表?
推论:Tim S的答案 - IEnumerable是协变的 - 解决了我的直接问题。 剩下一个小故障(我肯定有一个更简单的解决方案!)是如何进行反射。 这是我丑陋丑陋的丑陋黑客:
_collections = new List<IEnumerable<Device>>();
var fields = GetType().GetFields( BindingFlags.Instance | BindingFlags.Public );
foreach (var field in fields)
{
if (field.FieldType.Name.Contains( "DeviceCollection" ))
{
_collections.Add( (IEnumerable<Device>)field.GetValue(this) );
}
}
这,因为测试
if (field.FieldType == typeof(DeviceCollection<>)
不起作用。
宣言将是:
private List<IEnumerable<Device>> _collections;
并且您可以使用它(在设置之后,您似乎已经知道如何做),就像:
protected override IEnumerator<Device> enumerate()
{
return _collections.SelectMany(x => x).GetEnumerator();
}
这是因为IEnumerable<T>
接口是协变的 ,这意味着,例如IEnumerable<Switch>
( DeviceCollection<Switch>
implements)可以用作IEnumerable<Device>
。
DeviceCollection<Switch>
不能用作DeviceCollection<Device>
是类和集合不能协变 - 让您尝试将Device
Add
到ICollection<Switch>
,因为它应该只包含Switch
es。 但是从IEnumerable<Switch>
获取Device
是非常有意义的。
我认为你只需要一个清单:
public DeviceCollection<Device> Devices { get; private set; }
然后您可以使用Switches
返回特定类型,例如:
public IEnumerable<Switch> Switches
{
get
{
return this.Devices.OfType<Switch>();
}
}
所以现在enumerate
只是看起来像这样:
protected override IEnumerator<Device> enumerate()
{
foreach (var d in Devices)
{
yield return d;
}
}
你可以声明它:
private List<DeviceCollection<Device>> _collections;
为什么需要成员变量? 我想你可以做到
protected override IEnumerable<Device> enumerate()
{
... reflect to get properties of type IEnumerable<Device>
foreach (var prop in properties)
{
foreach (var device in (IEnumerable<Device>)prop.GetValue(this))
{
yield return device;
}
}
}
根据对效率的评论,虽然我不同意它们,并且也不同意使用单个List
和OfType
提出的解决方案,如果反射太慢/太危险,您可以简化原始代码:
public IEnumerable<Device> GetAll() {
return from list in new IEnumerable<Device>[] {Switches, Lights}
from device in list
select device;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.