繁体   English   中英

C#Generics - 收藏列表

[英]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 AddICollection<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;
        }
    }
}

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

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM