2

抽象クラスに基づくクラスの階層がありますDevice。すべてのデバイスのリポジトリは次のようになります。

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

where を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;

このリストを宣言するにはどうすればよいですか?


結果:IEnumerableが共変であるというTim Sの答えは、私の当面の問題を解決します。残りの小さな問題 (もっと簡単な解決策があるはずです!) は、反射を行う方法です。これが私の醜い醜いハックです:

_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>インターフェイスが共変であるため機能します。つまり、たとえばIEnumerable<Switch>(DeviceCollection<Switch>実装する) を として使用できますIEnumerable<Device>

DeviceCollection<Switch>aを an として使用できない理由DeviceCollection<Device>は、クラスとコレクションを共変にできないためです。 AddaDeviceを anICollection<Switch>にしようとしても意味がありませんSwitchDeviceしかし、 から を取得することは完全に理にかなっていますIEnumerable<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

次のように宣言できます。

private List<DeviceCollection<Device>> _collections;
于 2013-07-29T12:39:56.480 に答える
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 に答える