Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
233 views
in Technique[技术] by (71.8m points)

c# - Polymorphic Type Parameters in Generic Collections

Why does the C# compiler not allow polymorphic type (T) parameters in generic collections (ie, List[T]) ?

Take class 'A' and 'B' for example, where 'B' is a subclass of 'A'

class A { }
class B : A { }

and consider a function that takes a list of type 'A'

void f(List<A> aL) { }

that gets called with a list of type 'B'

List<B> bL = new List<B>();

f(bL);

The following error is given

ERROR: cannot convert from List<B> to List<A>

What semantic rule is being violated ?

Also is there an "elegant" mean to this end, aside from looping through and casting each element (I want some sugar please) ? Thanks.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

List<B> simply is not a subtype of List<A>. (I'm never sure about what "covariant" and what "contravariant" is in this context so I'll stick with "subtype".) Consider the case where you do this:

void Fun(List<A> aa) {
    aa(new A());
}

var bb = new List<B>();
Fun(bb); // whoopsie

If what you want to do was allowed it would be possible to add an A to a list of Bs which is clearly not type-safe.

Now, clearly it's possible to read elements from the list safely, which is why C# lets you create covariant (i.e. "read-only") interfaces - which let the compiler know it's not possible to cause this sort of corruption through them. If you only need read access, for collections, the usual one is IEnumerable<T>, so in your case you might just make the method:

void Fun(IEnumerable<A> aa) { ... }

and use the Enumerable methods - most should be optimised if the underlying type is List.

Unfortunately, because of how the C# generics stuff works, classes can't be variant at all, only interfaces. And as far as I know, all the collection interfaces "richer" than IEnumerable<T> are "read-write". You could technically make your own covariant wrapper interface that only exposes the read operations you want.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...