为什么(不真的吗?)名单,其中,T>实现所有这些接口,而不仅仅是IList的< T>?而不、仅仅是、接口、名单

2023-09-02 20:43:22 作者:学霸的芯

列表从MSDN声明:

 公共类List< T> :IList的< T&GT ;,的ICollection< T&GT ;,
 IEnumerable的< T&GT ;,的IList,ICollection的,IEnumerable的
 

反射器也能实现类似的图片。请问列表真正实现所有这些(如果是为什么)? 我已经检查了:

 接口I1 {}
    接口I2:I1 {}
    接口I3:I2 {}

    A级:I3 {}
    B类:I3,I2,I1 {}

    静态无效的主要(字串[] args)
    {
        VAR一个=新的A();
        VAR A1 =(I1)一个;
        变种A2 =(I 2)一个;
        VAR A3 =(I3)一个;

        变种B =新的B();
        VAR B1 =(I1)B:
        VAR B2 =(I2)B:
        VAR B3 =(I3)B:
    }
 

它编译。

[增订]:

大家好,据我所知,所有的答复留它:

 类节目
{

    接口I1 {}
    接口I2:I1 {}
    接口I3:I2 {}

    A级:I3 {}
    B类:I3,I2,I1 {}

    静态无效I1M(I1 I1){}
    静态无效I2M(I2 I2){}
    静态无效I3M(I3 I3){}

    静态无效的主要(字串[] args)
    {
        VAR一个=新的A();
        I1M(一);
        I2M(一);
        I3M(一);

        变种B =新的B();
        I1M(B);
        I2M(B);
        I3M(B);

        到Console.ReadLine();
    }
}
 
铅山一中 严密组织仿真高考,实现防疫考试零失误

将给予错误,但它编译和运行没有任何错误。为什么呢?

解决方案

更​​新:这个问题是的星期一2011年4月4日我的博客文章。感谢伟大的问题。

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

  

确实名单,其中,T> 真正实现所有这些接口

是的。

  

为什么?

由于在接口(例如,的IList< T> )从接口继承(比如的IEnumerable< T> ),则更多的派生接口的实施者必须也实现了更少的派生接口。这就是接口继承的意思;如果你满足更多的派生类型,那么你需要同时满足少派生类型的合同的合同。

  

因此​​,为了实现其基接口传递闭包的所有接口的所有方法一类是必需的?

没错。

  

是实现为更多的衍生接口,还需要在它的,它是实现所有那些不太衍生接口的基本类型列表声明一个类?

没有。

  

时的要求不是国家之类的?

没有。

  

所以这是的选的是否不太衍生实现的接口是在底座类型列表说明?

是的。

  

始终?

几乎总是:

 接口I1 {}
接口I2:I1 {}
接口I3:I2 {}
 

这是可选的I3是否说,它从I1继承。

  B类:I3 {}
 

实现I2和I1 I3的

实施者是必需的,但明确指出,他们这样做,他们不是必需的。这是可选的。

  D类:B {}
 

派生类不要求重新状态,它们实现来自它们的基类的接口,但允许这样做。 (这种情况是特殊的。请参阅下面的详细介绍)

 类℃下T>其中T:I3
{
    公共虚拟无效M< U>()其中U:I3 {}
}
 

键入相应给T参数,并实现I2和I1 U的要求,但它是可选的对T或U的约束地指出。

这始终是可选的在一个局部类重境界任何基本接口:

 部分E类:I3 {}
部分E类{}
 

的E后半部分可以声明它实现I3或I2或I1,但并不要求这样做。

  

好吧,我得到它;它是可选的。为什么有人会不必要地陈述一个基本接口?

也许是因为他们认为,这样做使得code更容易理解,更自我记录。

或者,也许是开发商写了code为

 接口I1 {}
接口I2 {}
接口I3:I1,I2 {}
 

和实现的,哦,等一下,I2应该继承自I1。为什么要制作的编辑则要求开发人员回去改I3的声明的没有的包含明确提到I1的?我认为没有理由强制要求开发者删除冗余信息。

  

除了是更容易阅读和理解,没有任何的技术的之间的显式声明的接口,基本类型列表,并离开它未说明,但隐含的区别是什么?

一般无,但可以在一个案例中一个微妙的差异。假设你有一个派生D类的基类B已经实施了一些接口。通过B.如果重新状态D的基类列表的接口,那么C#编译器会做一个的界面重新实施的ð自动实现这些接口。细节有点微妙的;如果你有兴趣的是如何工作的话,我建议仔细阅读C#4规范第13.4.6的。

  

请问名单,其中,T> 源$ C ​​$ C居然说出所有的接口

没有。实际的源$ C ​​$ C说:

 公共类List< T> :IList的< T&GT ;, System.Collections.IList
 

  

为什么MSDN有充分的接口列表,但真正的源头code不?

由于MSDN是文档;它应该给你,你可能希望尽可能多的信息。这是更清晰的文档,完成所有的,而不是让你通过十个不同的网页搜索,找出完整的接口集就是一个地方。

  

为什么反射显示整个列表?

反射只有元数据的工作。由于投入的完整列表是可选的,反光不知道原始出处code是否包含完整列表与否。这是更好地犯错的更多信息,一边。此外,反射器正试图帮助你展示你更多的信息,而不是隐藏你可能需要的信息。

  

奖金问题:为什么的IEnumerable< T> 的IEnumerable继承,但的IList< T> 不从的IList 继承?

整数序列可以被视为一个对象序列,通过拳击每一个整数,因为它出来的顺序。但是一个整数的读写列表不能被视为对象的读写名单,因为你可以把一个字符串转换成对象的读写列表。一个的IList< T> 不需要履行的IList 的整个合同,所以也没有继承它。

List declaration from MSDN:

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

Reflector gives similar picture. Does List really implement all of these (if yes why)? I have checked:

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

it compiles.

[UPDATED]:

Guys, as i understand, all the replies stay that it:

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

would give error, but it compiles and runs without any errors. Why?

解决方案

UPDATE: This question was the basis of my blog entry for Monday April 4th 2011. Thanks for the great question.

Let me break it down into many smaller questions.

Does List<T> really implement all those interfaces?

Yes.

Why?

Because when an interface (say, IList<T>) inherits from an interface (say IEnumerable<T>) then implementers of the more derived interface are required to also implement the less derived interface. That's what interface inheritance means; if you fulfill the contract of the more derived type then you are required to also fulfill the contract of the less derived type.

So a class is required to implement all the methods of all the interfaces in the transitive closure of its base interfaces?

Exactly.

Is a class that implements a more-derived interface also required to state in its base type list that it is implementing all of those less-derived interfaces?

No.

Is the class required to NOT state it?

No.

So it's optional whether the less-derived implemented interfaces are stated in the base type list?

Yes.

Always?

Almost always:

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

It is optional whether I3 states that it inherits from I1.

class B : I3 {}

Implementers of I3 are required to implement I2 and I1, but they are not required to state explicitly that they are doing so. It's optional.

class D : B {}

Derived classes are not required to re-state that they implement an interface from their base class, but are permitted to do so. (This case is special; see below for more details.)

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

Type arguments corresponding to T and U are required to implement I2 and I1, but it is optional for the constraints on T or U to state that.

It is always optional to re-state any base interface in a partial class:

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

The second half of E is permitted to state that it implements I3 or I2 or I1, but not required to do so.

OK, I get it; it's optional. Why would anyone unnecessarily state a base interface?

Perhaps because they believe that doing so makes the code easier to understand and more self-documenting.

Or, perhaps the developer wrote the code as

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

and the realized, oh, wait a minute, I2 should inherit from I1. Why should making that edit then require the developer to go back and change the declaration of I3 to not contain explicit mention of I1? I see no reason to force developers to remove redundant information.

Aside from being easier to read and understand, is there any technical difference between stating an interface explicitly in the base type list and leaving it unstated but implied?

Usually no, but there can be a subtle difference in one case. Suppose you have a derived class D whose base class B has implemented some interfaces. D automatically implements those interfaces via B. If you re-state the interfaces in D's base class list then the C# compiler will do an interface re-implementation. The details are a bit subtle; if you are interested in how this works then I recommend a careful reading of section 13.4.6 of the C# 4 specification.

Does the List<T> source code actually state all those interfaces?

No. The actual source code says

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

Why does MSDN have the full interface list but the real source code does not?

Because MSDN is documentation; it's supposed to give you as much information as you might want. It is much more clear for the documentation to be complete all in one place than to make you search through ten different pages to find out what the full interface set is.

Why does Reflector show the whole list?

Reflector only has metadata to work from. Since putting in the full list is optional, Reflector has no idea whether the original source code contains the full list or not. It is better to err on the side of more information. Again, Reflector is attempting to help you by showing you more information rather than hiding information you might need.

BONUS QUESTION: Why does IEnumerable<T> inherit from IEnumerable but IList<T> does not inherit from IList?

A sequence of integers can be treated as a sequence of objects, by boxing every integer as it comes out of the sequence. But a read-write list of integers cannot be treated as a read-write list of objects, because you can put a string into a read-write list of objects. An IList<T> is not required to fulfill the whole contract of IList, so it does not inherit from it.