[英]Why generic IList<> does not inherit non-generic IList
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>
顯然不是解決方案,因為沒有out
修飾符泛型不能分配給較少的繼承類; 並且創建List的新實例不是解決方案,因為有人可能想要IList<T>
實際引用作為IList
指針; 並使用List<T>
insured IList<T>
實際上是一個糟糕的編程實踐,並不適用於所有目的。
如果.NET想要提供靈活性, IList<T>
每個實現都不應該有非泛型實現的合同(即IList
)那么為什么他們沒有保留另一個實現泛型和非泛型版本的接口並且沒有建議所有希望簽訂通用和非遺傳項目的具體類別應通過該接口簽訂合同。
將ICollection<T>
為ICollection
並將IDictionary<TKey, TValue>
為IDictionary
時也會出現同樣的問題。
當你注意, T
中IList<T>
不是協變的 。 根據經驗:任何可以修改其狀態的類都不能協變。 原因是這些類通常具有將T
作為其參數之一的類型的方法,例如void Add(T element)
。 輸入位置不允許使用協變類型參數。
除其他原因外,還增加了仿制葯,以提供類型安全性。 例如,您無法將Elephant
添加到Apple
列表中。 如果ICollection<T>
要擴展ICollection
,那么你可以調用((ICollection)myApples).Add(someElephant)
而沒有編譯時錯誤,因為ICollection
有一個方法void Add(object obj)
,它似乎允許你添加列表中的任何對象,而在實踐中,您只能添加T
對象。 因此, ICollection<T>
不會擴展ICollection
, IList<T>
不會擴展IList
。
C#的創造者之一Anders Hejlsberg 解釋如下 :
理想情況下,所有通用集合接口(例如
ICollection<T>
,IList<T>
)將從其非通用對應物繼承,使得通用接口實例可以與通用和非通用代碼一起使用。事實證明,唯一可能的通用接口是
IEnumerable<T>
,因為只有IEnumerable<T>
是反變量[sic 1 ] :在IEnumerable<T>
,類型參數T
僅用於“輸出“位置(返回值)而不是”輸入“位置(參數)。ICollection<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>
,它Contains
和IndexOf
,以及一個可變的不變接口IMutableList<T>
。 然后你可以將IList<Apple>
為IList<object>
。 我在這里實現了這個:
M42集合 - 協變集合,列表和數組。
請注意,自2012年以來,在.NET 4.5及更高版本中,存在協變( out
修飾符)接口,
public interface IReadOnlyList<out T>
看它的文件 。
通常的集合類型如List<YourClass>
, Collection<YourClass>
和YourClass[]
確實實現了IReadOnlyList<YourClass>
並且由於協方差也可以用作IReadOnlyList<SomeBaseClass>
並最終用作IReadOnlyList<object>
。
正如您所猜測的那樣,您將無法通過IReadOnlyList<>
引用修改列表。
使用這個新界面,您可以一起避免使用非通用IList
。 但是,您仍然會遇到IReadOnlyList<T>
不是IList<T>
的基接口的問題。
創建一個接口MyIList<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();
}
}
您現在可以輕松看到的是,您必須雙重實現一堆方法。 一個用於T型,一個用於物體。 在正常情況下,你想避免這種情況。 這是共方差和反方差的問題。
已經給出了很好的答案。 關於IList的通知:
MSDN IList備注 :“IList實現分為三類:只讀,固定大小和可變大小。(...)。對於此接口的通用版本,請參閱System.Collections.Generic.IList<T>
“。
這有點誤導,因為在通用方面,我們將IList<T>
作為變量大小 ,並且IReadOnlyList<T>
作為只讀,因為4.5但是AFAIK,沒有固定大小的通用List。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.