C#泛型-集合列表

本文关键字:列表 集合 泛型 | 更新日期: 2023-09-27 18:25:31

我有一个基于抽象Device类的类层次结构。所有设备的存储库看起来有点像这样:

class Hardware
{
    public readonly DeviceCollection<Switch> Switches = ...;
    public readonly DeviceCollection<Light> Lights = ...;
}

其中CCD_ 2实现CCD_。

我需要在所有设备上枚举,而我目前糟糕的代码完成了这个

    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<>)

不起作用。

C#泛型-集合列表

声明为:

private List<IEnumerable<Device>> _collections;

你可以像一样简单地使用它(在设置好它之后,你似乎已经很清楚如何使用了)

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

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

DeviceCollection<Switch>不能用作DeviceCollection<Device>的原因是类和集合不能是协变的-让你尝试从AddDeviceICollection<Switch>是没有意义的,因为它应该只包含Switches。但从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;
        }
    }
}

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

public IEnumerable<Device> GetAll() {
    return from list in new IEnumerable<Device>[] {Switches, Lights}
           from device in list
           select device;
}