[英]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.