我有一个基于抽象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>的原因是类和集合不能是协变的-让您尝试将AddDevice转换为,因为它只能包含ICollection<Switch> es。但是从Switch中取出Device是很有意义的。

10-07 18:05