15

IList<T>は継承IListしません。IEnumerable<out T>IEnumerable

out修飾子が唯一の理由である場合、 IList<T>(たとえばCollection<T>List<T>)の実装のほとんどがIListインターフェイスを実装する理由です。

したがって、そのステートメントが のすべての実装に当てはまる場合は、必要に応じIList<T>て直接キャストすることができIListます。ただし、問題はIList<T>継承しないIListため、すべてのIList<T>オブジェクトがIList.

さらに、修飾子がないとジェネリックを継承の少ないクラスに割り当てることができないIList<object>ため、使用は明らかに解決策ではありません。outList の新しいインスタンスを作成することは、ここでは解決策ではありません。これは、ポインターIList<T>としての実際の参照が必要な場合があるためです。IListuse List<T>insteed ofIList<T>は、実際には悪いプログラミング手法であり、すべての目的に役立つわけではありません。

IList<T>.NET が、すべての実装に非ジェネリック実装のコントラクト (つまり) を持たないという柔軟性を与えたい場合IList、なぜジェネリック バージョンと非ジェネリック バージョンの両方を実装する別のインターフェイスを保持せず、すべてが具体的であることを示唆しなかったのかジェネリックおよび非ジェネティック アイテムの契約を希望するクラスは、そのインターフェイスを介して契約する必要があります。

ICollection<T>へのキャストでも同じ問題が発生します。ICollectionIDictionary<TKey, TValue>IDictionary

4

4 に答える 4

9

ご指摘のとおり、TinIList<T>共変ではありません。経験則として、その状態を変更できるクラスは共変ではありません。Tその理由は、そのようなクラスには多くの場合、パラメータの 1 つの型としてを持つメソッドがあるためですvoid Add(T element)。また、共変型パラメーターは入力位置では使用できません。

ジェネリックスが追加された理由は他にもありますが、タイプ セーフを提供するためです。たとえば、Elephantを のリストに追加することはできませんAppleICollection<T>を拡張する場合は、メソッドのように、コンパイル時エラーなしでICollection呼び出すことができます。これにより、リストに任意のオブジェクトを追加できるように見えますが、実際には のオブジェクトのみを追加できます。したがって、拡張されず、拡張されません。((ICollection)myApples).Add(someElephant)ICollectionvoid Add(object obj)TICollection<T>ICollectionIList<T>IList

C# の作成者の 1 人である Anders Hejlsberg は、次のように説明しています

理想的には、すべてのジェネリック コレクション インターフェイス (例: ) はICollection<T>IList<T>ジェネリック インターフェイス インスタンスをジェネリック コードと非ジェネリック コードの両方で使用できるように、対応する非ジェネリック インターフェイスから継承します。

結局のところ、これが可能な唯一のジェネリック インターフェイスは、反変IEnumerable<T>のみであるためです[sic 1 ] : では、型パラメーターは「出力」位置 (戻り値) でのみ使用され、「入力」では使用されません。位置 (パラメータ)。入力位置と出力位置の両方で使用するため、これらのインターフェイスは不変です。IEnumerable<T>IEnumerable<T>TICollection<T>IList<T>T

1 )IEnumerable<T>変です


.Net 4.5 以降、IReadOnlyCollection<out T>およびIReadOnlyList<out T>共変インターフェイスがあります。しかしIList<T>ICollection<T>リストとコレクション クラスの多くは、それらを実装または拡張しません。Count率直に言って、これらは と しか定義していないため、あまり役に立ちませんthis[int index]


.Net 4.5 をゼロから再設計できた場合、リスト インターフェイスを、 と を含む読み取り専用の共変インターフェイスと、変更可能な不変インターフェイスに分割したIList<out T>でしょう。次に、にキャストできます。ここでこれを実装しました:ContainsIndexOfIMutableList<T>IList<Apple>IList<object>

M42 コレクション- 共変のコレクション、リスト、および配列。

于 2014-01-18T11:34:54.103 に答える
3

out2012 年以降、.NET 4.5 以降では、共変 (修飾子) インターフェイスが存在することに注意してください。

public interface IReadOnlyList<out T>

そのドキュメントを参照してください。

やのような通常のコレクション タイプは、共分散のために および を実装し、 最終的に としてList<YourClass>Collection<YourClass>使用YourClass[]できます。IReadOnlyList<YourClass>IReadOnlyList<SomeBaseClass>IReadOnlyList<object>

ご想像のとおり、参照によってリストを変更することはできませんIReadOnlyList<>

IListこの新しいインターフェースを使用すると、一般的ではないものをまとめて回避できる場合があります。ただしIReadOnlyList<T>、 の基本インターフェイスではないという問題は引き続き発生しますIList<T>

于 2014-01-18T11:12:58.603 に答える
1

インターフェイスを作成し、andMyIList<T>から継承させます。IList<T>IList

public interface MyIList<T> : IList<T>, IList
{ }

クラスMySimpleListを作成して実装させますMyIList<T>

public class MySimpleList<T> : MyIList<T>
{
    public int Count
    {
        get { throw new NotImplementedException(); }
    }

    public bool IsFixedSize
    {
        get { throw new NotImplementedException(); }
    }

    public bool IsReadOnly
    {
        get { throw new NotImplementedException(); }
    }

    public bool IsSynchronized
    {
        get { throw new NotImplementedException(); }
    }

    public object SyncRoot
    {
        get { throw new NotImplementedException(); }
    }

    object IList.this[int index]
    {
        get
        {
            throw new NotImplementedException();
        }
        set
        {
            throw new NotImplementedException();
        }
    }

    public T this[int index]
    {
        get
        {
            throw new NotImplementedException();
        }
        set
        {
            throw new NotImplementedException();
        }
    }

    public void Add(T item)
    {
        throw new NotImplementedException();
    }

    public int Add(object value)
    {
        throw new NotImplementedException();
    }

    public void Clear()
    {
        throw new NotImplementedException();
    }

    public bool Contains(T item)
    {
        throw new NotImplementedException();
    }

    public bool Contains(object value)
    {
        throw new NotImplementedException();
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        throw new NotImplementedException();
    }

    public void CopyTo(Array array, int index)
    {
        throw new NotImplementedException();
    }

    public IEnumerator<T> GetEnumerator()
    {
        throw new NotImplementedException();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        throw new NotImplementedException();
    }

    public int IndexOf(T item)
    {
        throw new NotImplementedException();
    }

    public int IndexOf(object value)
    {
        throw new NotImplementedException();
    }

    public void Insert(int index, T item)
    {
        throw new NotImplementedException();
    }

    public void Insert(int index, object value)
    {
        throw new NotImplementedException();
    }

    public bool Remove(T item)
    {
        throw new NotImplementedException();
    }

    public void Remove(object value)
    {
        throw new NotImplementedException();
    }

    public void RemoveAt(int index)
    {
        throw new NotImplementedException();
    }
}

ここで簡単にわかることは、一連のメソッドを二重に実装する必要があるということです。1 つはタイプ T 用で、もう 1 つはオブジェクト用です。通常の状況では、これを回避する必要があります。これは、共分散と反分散の問題です。

あなたが見つけることができる最良の説明 (IList と IList に関するこの具体的な問題については、質問のコメント内で Jon によって既に言及されているBrad の記事です。

于 2013-01-28T09:45:17.137 に答える
0

良い答えはすでに与えられています。ただし、IList に関する通知:

MSDN IList の解説: 「IList の実装は、読み取り専用、固定サイズ、可変サイズの 3 つのカテゴリに分類されます。(...) このインターフェイスの汎用バージョンについては、を参照してください System.Collections.Generic.IList<T>。」

ジェネリック側では可変サイズであり、4.5以降は読み取り専用ですが、AFAIKでは固定サイズのジェネリックリストがないため、これは少し誤解IList<T>招きますIReadOnlyList<T>

于 2016-11-09T09:07:29.473 に答える