问题
如果我有一个定义如下的类:
public class TestList : ObservableCollection<TestClass>, IList<ITestInterface>, ICollection<ITestInterface>, IEnumerable<ITestInterface>, IEnumerable
我不能在此类的对象上使用 IEnumerable
扩展方法(System.Linq
命名空间)。
注意事项
ObservableCollection
中的类型与集合接口(interface)中的类型不同,但TestClass
确实实现了ITestInterface
- 仍然可以
foreach
TestList
对象中的项目。这样做会为项提供TestClass
的类型
- 我在文件顶部有一个
using
语句用于System.Linq
这显然与 ObservableCollection
中的类型与类定义中的其他类型不同这一事实有关,但为什么呢?集合仍然可以在 foreach
语句中枚举,这基本上就是那些扩展方法所做的(当然还有额外的逻辑)。
问题
为什么创建一个继承自一种类型的集合并实现另一种类型的集合接口(interface)的类会导致 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-part-ten-dealing-with-ambiguity/)
你所处的特别糟糕的情况是,正如其他答案所指出的那样,重载决策的类型推断步骤无法推断出 First<T>
的类型参数是什么应该是因为有两个不兼容的选项。一个较小的 repro 是:
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 上报告一个问题,也许这个问题可以得到解决。
关于c# - 从集合继承和实现可枚举接口(interface)时,IEnumerable 扩展方法 (System.Linq) 不可用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50140779/