[英]IEnumerable extension methods (System.Linq) unavailable when inheriting from collection and implementing enumerable interfaces
問題
如果我有一個這樣的定義類:
public class TestList : ObservableCollection<TestClass>, IList<ITestInterface>, ICollection<ITestInterface>, IEnumerable<ITestInterface>, IEnumerable
我不能在此類的對象上使用IEnumerable
擴展方法( System.Linq
命名空間)。
注意事項
ObservableCollection
中的類型與collection接口中的類型不同,但是TestClass
確實實現了ITestInterface
foreach
TestList
對象中的項目。 這樣做使項目具有TestClass
類型 System.Linq
的using
語句 顯然這與ObservableCollection
中的類型不同於類定義中的其他類型有關,但是為什么呢? 仍然可以在foreach
語句中枚舉該集合,這基本上就是那些擴展方法仍然要做的事情(當然還有其他邏輯)。
題
為什么創建從一種類型的集合繼承並實現另一種類型的集合接口的類會導致System.Linq
的擴展方法無法用於該類的對象?
最小,完整和可驗證的示例
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
var t = new TestList();
t.First(); //Extension method unavailable, compiler error
foreach (var item in t)
{
//item is of type TestClass
}
}
}
public interface ITestInterface { }
public class TestClass : ITestInterface { }
public class TestList : System.Collections.ObjectModel.ObservableCollection<TestClass>, IList<ITestInterface>, ICollection<ITestInterface>, IEnumerable<ITestInterface>, IEnumerable
{
//Method implementations are unnecessary for the example
ITestInterface IList<ITestInterface>.this[int index] { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public bool IsReadOnly => throw new NotImplementedException();
public void Add(ITestInterface item) => throw new NotImplementedException();
public bool Contains(ITestInterface item) => throw new NotImplementedException();
public void CopyTo(ITestInterface[] array, int arrayIndex) => throw new NotImplementedException();
public int IndexOf(ITestInterface item) => throw new NotImplementedException();
public void Insert(int index, ITestInterface item) => throw new NotImplementedException();
public bool Remove(ITestInterface item) => throw new NotImplementedException();
IEnumerator<ITestInterface> IEnumerable<ITestInterface>.GetEnumerator() => throw new NotImplementedException();
}
}
背景
對於那些好奇的人,我在使用Telerik RadGridView控件(WPF)時碰到了這一點。 我試圖在網格的Columns
屬性上使用.First()
擴展方法,但是Columns
屬性的類型具有與我上面提供的類定義類似的類定義。
首先, 請不要這樣做 。 您已經在同一類型上實現了IEnumerable<TestClass>
和IEnumerable<ITestInterface>
,這可能會導致一些非常討厭的問題。
例如: IEnumerable<T>
是協變的,因此,如果將類型轉換為IEnumerable<Object>
,會發生什么? 對象序列是否僅包含TestClass
對象,還是可以包含實現ITestInterface
任何對象? 很難說! (有關這一點的擴展討論,請參閱我在2007年關於該主題的文章中的評論: https : //blogs.msdn.microsoft.com/ericlippert/2007/11/09/covariance-and-contravariance-in-c-含糊不清的十部分交易/ )
正如其他答案所指出的那樣,您所處的特別討厭的情況是,重載解析的類型推斷步驟無法推斷出First<T>
的類型參數應該是什么,因為有兩個不兼容的選項。 一個較小的復制將是:
public class C : IEnumerable<string>, IEnumerable<object>
{
IEnumerator<string> IEnumerable<string>.GetEnumerator() => null;
IEnumerator<object> IEnumerable<object>.GetEnumerator() => null;
IEnumerator IEnumerable.GetEnumerator() => null;
}
現在, new C().First()
給您同樣的錯誤。
錯誤消息很可怕,對此我深表歉意。 常見的情況是,沒有這種適用的擴展方法,並且針對該情況優化了錯誤消息。 這本來是更好的檢測,沒有適用的擴展方法的原因是由於類型推斷失敗。 在其他錯誤報告場景中,我這樣做了,但沒有這樣做。
考慮在GitHub上報告問題,也許這個問題可以解決。
原因是編譯器沒有足夠的類型信息來明確地推斷通用First<T>()
方法的類型參數。 在這種情況下,可能的類型參數是TestClass
和ITestInterface
。
您可以通過顯式指定type參數來幫助編譯器。 將編譯以下內容:
var item1 = t.First<TestClass>();
var item2 = t.First<ITestInterface>();
是的,編譯器本可以產生更好的錯誤消息。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.