c# - 从集合继承和实现可枚举接口(interface)时,IEnumerable 扩展方法 (System.Linq) 不可用

标签 c# linq ienumerable linq-extensions

问题
如果我有一个定义如下的类:

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/

相关文章:

c# - C# 中的简单循环(移动平均)数组

c# - 将 IEnumerable 转换为属性列表

c# - 使用 iTextsharp 检查 pdf 中的复选框字段的常用方法是什么?

linq - Anglesharp - 如何从 Yelp 网站获得评分和评论?

c# - 错误消息 : "Only primitive types or enumeration types are supported in this context."

c# - LINQ:动态 Where 子句切换大小写组合

c# - 如果长度恰好为一个,或者否则为默认值,我如何获得 IEnumerable 的第一个值?

c# - 为什么使用 .AsEnumerable() 而不是强制转换为 IEnumerable<T>?

c# - dapper 执行存储过程时将数据类型日期时间转换为小日期时间时出错

c# - 如何在 linq 中选择多个条件?