我有一个基于抽象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>
接口是covariant,这意味着例如IEnumerable<Switch>
(由DeviceCollection<Switch>
实现)可以用作IEnumerable<Device>
。DeviceCollection<Switch>
不能用作DeviceCollection<Device>
的原因是类和集合不能是协变的-让您尝试将Add
从Device
转换为,因为它只能包含ICollection<Switch>
es。但是从Switch
中取出Device
是很有意义的。