Why (actually?) Does the <T> list implement all of these interfaces, not just IList <T>?
List Announcement from MSDN:
public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable The reflector gives a similar picture. Does List really implement all this (if so, why)? I 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 it, all the answers remain:
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(); } } will give an error, but it compiles and starts without errors. Why?
UPDATE: This question was the basis of my blog post on Monday April 4, 2011 . Thanks for the great question.
Let me break it down into many smaller questions.
Does
List<T>really implement all of these interfaces?
Yes.
Why?
Because when an interface (say IList<T> ) inherits from an interface (say IEnumerable<T> ), developers of a more derived interface also need to implement a less derived interface. What does interface inheritance mean? if you are executing a contract of a more derived type, then you also need to execute a contract of a less derived type.
So, does a class need to implement all the methods of all interfaces in the transitive closure of its base interfaces?
That's right.
Is a class that implements a more derived interface should also indicate in its list of base type that it implements all of these less derived interfaces?
No.
Is a class required to NOT indicate it?
No.
So, optionally, are less implemented interfaces listed in the base type list?
Yes.
Always?
Almost always:
interface I1 {} interface I2 : I1 {} interface I3 : I2 {} It is not necessary whether I3 claims to inherit from I1.
class B : I3 {} Implementations of I3 are necessary for the implementation of I2 and I1, but they do not need to explicitly indicate that they are doing this. It's not obligatory.
class D : B {} Derived classes are not required to re-indicate that they implement an interface from their base class, but they are allowed 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 for restrictions on T or U it is not necessary to indicate that.
It is always optional to reinstall any base interface in a partial class:
partial class E : I3 {} partial class E {} In the second half, E is allowed to indicate that it implements I3 or I2 or I1, but is not required for this.
OK, I understand; it's not obligatory. Why would anyone unnecessarily specify a base interface?
Perhaps because they believe this makes the code more understandable and more self-documented.
Or perhaps the developer wrote the code as
interface I1 {} interface I2 {} interface I3 : I1, I2 {} and implemented, oh wait a minute, I2 must inherit from I1. Why does this editing require the developer to come back and modify the I3 declaration so as not to explicitly mention I1? I see no reason to force developers to remove redundant information.
Besides being easier to read and understand, is there any technical difference between explicitly specifying an interface in the base type list and leaving it uninstalled, but implied?
Usually not, but in one case there may be a subtle difference. Suppose you have a derived class D, whose base class B has implemented some interfaces. D automatically implements these interfaces through B. If you redefine the interfaces in the list of base class D, then the C # compiler will reimplement the interface. The details are a little thin; if you are interested in how this works, I recommend that you carefully read section 13.4.6 of the C # 4 specification.
Does
List<T>source code indicate all of these interfaces?
No. Actual source code says
public class List<T> : IList<T>, System.Collections.IList Why does MSDN have a complete list of interfaces but no real source code?
Because MSDN is documentation; he should give you as much information as you want. It is much more clear that the documentation will be complete in one place than to search through ten different pages to find out what a complete set of interfaces is.
Why does Reflector display the entire list?
Reflector has only metadata to work with. Since including a complete list is optional, Reflector has no idea whether the source code contains a complete list or not. It is better to err on the side of additional information. Again, Reflector is trying to help you by showing you more information, rather than hiding the information you might need.
BONUS QUESTION: Why does
IEnumerable<T>inherit fromIEnumerable, butIList<T>does not inherit fromIList?
A sequence of integers can be considered as a sequence of objects, by boxing each integer when it leaves the sequence. But the list of integers for reading and writing cannot be considered as a list of objects for reading and writing, because you can put a string in the list of objects for reading and writing. IList<T> not required to execute the entire IList contract, so it is not inherited from it.
Non-generic interfaces are designed for backward compatibility. If you write code using generics and want to transfer your list to some module written in .NET 1.0 (which does not have generics), you still want it to be successful. Therefore, IList, ICollection, IEnumerable .
Great question and great answer from Eric Lippert. This question came to my mind in the past, and my next understanding, I hope this helps you.
Suppose I am a C # programmer on another planet in the universe, on this planet all animals can fly. Therefore, my program looks like this:
interface IFlyable { void Fly(); } interface IAnimal : IFlyable { //something } interface IBird : IAnimal, IFlyable { } Well, you might be confused, since Birds are Animals , and all Animals can fly, why do we need to specify IFlyable in the IBird interface? OK Let me change it to:
interface IBird : IAnimal { } I am 100% sure that the program works as before, so nothing has changed? IFlyable in IFlyable 's interface completely useless? Go on.
Once my company sold software to another company on Earth. But on Earth, not all animals can fly! Therefore, of course, we need to change the IAnimal interface and the classes that implement it. After the modification, I found that IBird now incorrect, because birds in the ground can fly! Now I'm sorry to remove IFlyable from IBird !
- Yes, they do, because
List<T>can have properties and a method for executing all of these interfaces. You do not know which interface someone will declare as a parameter or return value, therefore, the more interface packages are implemented, the more universal it can be. - Your code will compile because upcasting (
var a1 = (I1)a;) fails with an error at runtime, and not at compile time. I can dovar a1 = (int)aand compile it.
List<> implements all of these interfaces to expose the functionality described by the various interfaces. It includes general list, collection, and enumeration functions (backward compatible with nonequivalent equivalents)