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<>)
不起作用。
小时>声明为:
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>
的原因是类和集合不能是协变的-让你尝试从Add
、Device
到ICollection<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;
}
}
}
根据关于效率的评论,虽然我不同意他们的观点,也不同意使用单个DeviceCollection
0和OfType
的解决方案,但如果反射太慢/太危险,你可以简化你的原始代码:
public IEnumerable<Device> GetAll() {
return from list in new IEnumerable<Device>[] {Switches, Lights}
from device in list
select device;
}