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();
    }
}

将给出错误,但它编译和运行时没有任何错误.为什么?

推荐答案

更新:这个问题是my blog entry for Monday April 4th 2011的基础.谢谢你的提问.

让我把它分解成许多小问题.

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,但不需要这样做.

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

也许是因为他们相信这样做会让代码更容易理解,更容易self 记录.

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

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

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

除了更容易阅读和理解之外,在基类型列表中显式地声明接口和不声明但隐含接口之间有什么区别吗?

通常不会,但在一种情况下可能会有细微的不同.假设您有一个派生类D,其基类B实现了一些接口.D通过B自动实现这些接口.如果您在D的基类列表中重新声明这些接口,那么C#编译器将执行interface re-implementation.细节有点微妙;如果您对它的工作原理感兴趣,那么我建议您仔细阅读C#4规范的13.4.6节.

List<T>个源代码真的声明了所有这些接口吗?

不是的. The actual source code says

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

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

因为MSDN是文档;它应该提供你想要的信息.比起让你搜索十个不同的页面来找出完整的界面集,在一个地方完成所有文档要清楚得多.

为什么Reflector会显示整个列表?

Reflector只有元数据可供使用.由于放入完整列表是可选的,Reflector不知道原始源代码是否包含完整列表.最好是在更多信息方面犯错.同样,Reflector试图通过向您显示更多信息而不是隐藏您可能需要的信息来帮助您.

额外问题:为什么IEnumerable<T>IEnumerable继承而IList<T>IList继承?

一个整数序列可以被视为一个对象序列,方法是将序列中的每个整数装箱.但整数的读写列表不能被视为对象的读写列表,因为可以将字符串放入对象的读写列表中.IList<T>不需要履行IList的全部合同,因此它不是从中继承的.

.net相关问答推荐

.NET 7,8 System.Text.Json反序列化多态参数

.NET模拟具有泛型返回类型的方法

C#:如何构造异步/等待代码,其中许多请求是针对相同的、返回缓慢的数据发出的,这可以满足所有请求

为什么 PropertyInfo.SetValue 在此示例中不起作用以及如何使其起作用?

.NET MAUI ListView - ObservableCollection - 在异步方法期间不更新

如何调试 .NET 运行时中的内部错误?

RNGCryptoServiceProvider 的优缺点

为什么要判断这个!= null?

共享 AssemblyInfo 用于跨解决方案的统一版本控制

HashSet 是否保留插入顺序?

C# 属性实际上是方法吗?

如何使用配置转换删除 ConnectionString

有没有办法从方法返回匿名类型?

在 C#/.NET 中组合路径和文件名的最佳方法是什么?

从流中获取 TextReader?

如何访问 Session 变量并在 javascript 中设置它们?

是否可以判断对象是否已附加到实体框架中的数据上下文?

C# 应用程序中的全局键盘捕获

在 IIS 中访问 .svc 文件时出现 HTTP 404

如何为 Dapper 查询动态创建参数