2

我有一个基于抽象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<>)

不起作用。

4

4 回答 4

8

声明将是:

private List<IEnumerable<Device>> _collections;

你可以使用它(在设置它之后,你似乎已经知道如何做)很容易:

protected override IEnumerator<Device> enumerate()
{
    return _collections.SelectMany(x => x).GetEnumerator();
}

这是可行的,因为IEnumerable<T>接口是协变的,这意味着,例如 an IEnumerable<Switch>DeviceCollection<Switch>实现)可以用作IEnumerable<Device>.

aDeviceCollection<Switch>不能用作 an的原因DeviceCollection<Device>是类和集合不能是协变的——让你尝试AddaDevice到 an是没有意义的ICollection<Switch>,因为它应该只包含Switches。但是DeviceIEnumerable<Switch>.

于 2013-07-29T13:03:12.043 回答
2

我认为你只需要一个列表:

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;
    }
}
于 2013-07-29T12:39:36.230 回答
0

为什么需要成员变量?我想你可以做

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;
        }
    }
}

根据关于效率的评论,虽然我不同意他们,也不同意使用单个Listand提出的解决方案OfType,如果反射太慢/危险,您可以简化原始代码:

public IEnumerable<Device> GetAll() {
    return from list in new IEnumerable<Device>[] {Switches, Lights}
           from device in list
           select device;
} 
于 2013-07-29T12:51:05.443 回答
0

你可以声明它:

private List<DeviceCollection<Device>> _collections;
于 2013-07-29T12:39:56.480 回答