왜 (정말입니까?) 목록 IList뿐만 아니라 이러한 모든 인터페이스를 구현하십시오.?
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();
}
}
오류가 발생하지만 오류없이 컴파일되고 실행됩니다. 왜?
업데이트 :이 질문은 2011 년 4 월 4 일 월요일 내 블로그 항목 의 기초였습니다 . 좋은 질문에 감사드립니다.
여러 가지 작은 질문으로 나누어 보겠습니다.
List<T>
이러한 모든 인터페이스를 실제로 구현 합니까 ?
예.
왜?
인터페이스 (예 IList<T>
:)가 인터페이스 (예 :)에서 상속 될 때 IEnumerable<T>
더 많이 파생 된 인터페이스의 구현자는 덜 파생 된 인터페이스도 구현해야합니다. 이것이 인터페이스 상속이 의미하는 바입니다. 더 많이 파생 된 유형의 계약을 이행하는 경우 덜 파생 된 유형의 계약도 이행해야합니다.
그렇다면 클래스는 기본 인터페이스의 전 이적 폐쇄에서 모든 인터페이스의 모든 메서드를 구현해야합니까?
바로 그거죠.
더 많이 파생 된 인터페이스를 구현하는 클래스도 기본 유형 목록에 덜 파생 된 인터페이스를 모두 구현하고 있음을 명시해야합니까?
아니.
수업이 그것을 명시하지 않아도 되나요?
아니.
따라서 덜 파생 된 구현 된 인터페이스가 기본 유형 목록에 명시되어 있는지 여부는 선택 사항 입니까?
예.
항상?
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 fromIEnumerable
butIList<T>
does not inherit fromIList
?
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.
The non-generic interfaces are for backward compatibility. If you write code using generics and want to pass your list to some module written in .NET 1.0 (which doesn't have generics) you still want this to succeed. Hence IList, ICollection, IEnumerable
.
Great question and great answer from Eric Lippert. This question came to my mind in the past, and the following is my understanding, hope it helps you.
Suppose I'm a C# programmer in another planet in the universe, in this planet, all animals can fly. So my program looks like:
interface IFlyable
{
void Fly();
}
interface IAnimal : IFlyable
{
//something
}
interface IBird : IAnimal, IFlyable
{
}
Well you may be confused, since Birds
are Animals
, and all Animals
can fly, why do we need to specify IFlyable
in the interface IBird
? OK Let's change it to:
interface IBird : IAnimal
{
}
I'm 100% sure that the program works like before, so nothing is changed? The IFlyable
in the interface IBird
is totally useless? Let's go on.
One day, my company sold the software to another company on the Earth. But on the Earth, not all animals can fly! So of course, we need to modify the interface IAnimal
and the classes that implement it. After the modification, I found that IBird
is incorrect now because birds in earth can fly! Now I regret removing IFlyable
from IBird
!
- Yes they do, because
List<T>
can has the properties and method to fulfill all those interfaces. You don't know what interface someone is going to have declared as a parameter or return value, so the more interfaces list implements, the more versatile it can be. - Your code will compile because upcasting (
var a1 = (I1)a;
) fails at runtime not compile time. I can dovar a1 = (int)a
and have it compile.
List<>
implements all of those interfaces in order to expose the functionality described by the different interfaces. It encompasses the features of a generic List, Collection and Enumerable (with backwards compatibility to the non-generic equivalents)
'Nice programing' 카테고리의 다른 글
Elixir에서 맵 변수로 구조체를 어떻게 초기화합니까? (0) | 2020.11.10 |
---|---|
wpf ComboBox DisplayMemberPath, SelectedValue 및 SelectedValuePath와 혼동 (0) | 2020.11.10 |
날짜를 확인하는 방법은 무엇입니까? (0) | 2020.11.10 |
NSString = 특정 문자열 값인지 확인하는 방법? (0) | 2020.11.10 |
ISO C90은 C에서 혼합 된 선언과 코드를 금지합니다. (0) | 2020.11.10 |