一尘不染

为什么(确实吗?)列表 实现所有这些接口,而不仅仅是IList?

c#

List 来自MSDN的声明:

public class List<T> : IList<T>, ICollection<T>, 
 IEnumerable<T>, IList, ICollection, IEnumerable

反射镜给出相似的图像。难道List真的实现所有这些(如果是为什么)?我检查过:

    interface I1 {}
    interface I2 : I1 {}
    interface I3 : I2 {}

    class A : I3 {}
    class B : I3, I2, I1 {}

    static void Main(string[] args)
    {
        var a = new A();
        var a1 = (I1)a;
        var a2 = (I2)a;
        var a3 = (I3)a;

        var b = new B();
        var b1 = (I1) b;
        var b2 = (I2)b;
        var b3 = (I3)b;
    }

它编译。

[更新]:

伙计们,据我所知,所有答复都保持不变:

class Program
{

    interface I1 {}
    interface I2 : I1 {}
    interface I3 : I2 {}

    class A : I3 {}
    class B : I3, I2, I1 {}

    static void I1M(I1 i1) {}
    static void I2M(I2 i2) {}
    static void I3M(I3 i3) {}

    static void Main(string[] args)
    {
        var a = new A();
        I1M(a);
        I2M(a);
        I3M(a);

        var b = new B();
        I1M(b);
        I2M(b);
        I3M(b);

        Console.ReadLine();
    }
}

会给出错误,但是它可以编译并运行而没有任何错误。为什么?


阅读 187

收藏
2020-05-19

共1个答案

一尘不染

更新:这个问题是我2011年4月4日星期一博客条目的基础。感谢您提出的重大问题。

让我将其分解为许多较小的问题。

是否List<T>真的实现了所有这些接口?

是。

为什么?

因为当一个接口(例如IList<T>)从一个接口(例如)继承时,IEnumerable<T>则需要更多派生接口的实现者也必须实现不那么派生接口。这就是接口继承的含义;如果您履行派生类型较多的合同,则还需要履行派生类型较少的合同。

因此,需要一个类在其基本接口的传递性关闭中实现所有接口的所有方法吗?

究竟。

是否还需要一个实现更高派生的接口的类在其基本类型列表中声明它正在实现所有那些那些更低派生的接口?

没有。

是否要求该类不声明它?

没有。

因此,它是 可选 是否衍生少实现的接口在基本类型列表说明?

是。

总是?

几乎总是:

interface I1 {}
interface I2 : I1 {}
interface I3 : I2 {}

I3是否声明它继承自I1是可选的。

class B : I3 {}

I3的实现者需要实现I2和I1,但是不需要他们明确声明自己正在这样做。它是可选的。

class D : B {}

不需要派生类重新声明它们从其基类实现了一个接口,但是允许这样做。(这种情况很特殊;有关更多详细信息,请参见下文。)

class C<T> where T : I3
{
    public virtual void M<U>() where U : I3 {}
}

要实现I2和I1,需要与T和U对应的类型实参,但是对于T或U的约束条件,它是可选的。

在部分类中重新声明任何基本接口始终是可选的:

partial class E : I3 {}
partial class E {}

E的后半部分可以声明其实现了I3或I2或I1,但并非必须如此。

好的我明白了; 这是可选的。为什么有人会不必要地声明基本接口?

也许是因为他们相信这样做会使代码更易于理解和更多地自我记录。

或者,也许开发人员将代码编写为

interface I1 {}
interface I2 {}
interface I3 : I1, I2 {}

然后意识到,哦,等等,I2应该继承自I1。为什么要进行该编辑然后要求开发人员返回并更改I3的声明以
包含对I1的明确提及?我没有理由强迫开发人员删除多余的信息。

除了更易于阅读和理解之外,在基本类型列表中显式声明一个接口与保留未声明但隐含的接口之间是否存在 技术上的 区别?

通常不会,但是在一种情况下可能会有细微的差异。假设您有一个派生类D,其基类B实现了一些接口。D通过B自动实现这些接口。如果在D的基类列表中重新声明接口,则C#编译器将
重新实现接口 。细节有些微妙。如果您对它的工作方式感兴趣,那么我建议您仔细阅读C#4规范的13.4.6节。

List<T>源代码实际上是否声明了所有这些接口?

否。实际的源代码说

public class List<T> : IList<T>, System.Collections.IList

为什么MSDN具有完整的接口列表,而真正的源代码却没有?

因为MSDN是文档;它应该为您提供尽可能多的信息。与将您搜索十个不同的页面以找出完整的界面集相比,使文档在一个地方完整地完成要清楚得多。

为什么Reflector显示整个列表?

Reflector仅具有可使用的元数据。由于放入完整列表是可选的,因此Reflector不知道原始源代码是否包含完整列表。最好在更多信息方面有所帮助。再次,Reflector尝试通过向您显示更多信息而不是隐藏您可能需要的信息来帮助您。

奖金问题:为什么IEnumerable<T>继承自IEnumerableIList<T>不继承自IList

通过将整数从序列中装箱,可以将整数序列视为对象序列。但是,不能将整数的读写列表视为对象的读写列表,因为您可以将字符串放入对象的读写列表中。IList<T>不需要An
来履行的整个合同IList,因此它不会继承自。

2020-05-19