Reputation: 3915
To me it makes perfect to do like that:
public class A { }
public class B : A { }
public class C
{
public List<A> b = new List<B>();
}
List expects elements to be of class A, which is also true for List. I know there is a type mismatch, but logically it makes perfect sense for compiler to allow such mismatch. Why not?
Upvotes: 0
Views: 110
Reputation: 5760
You can't achieve this using classes. You can using interfaces (look up covariance and contravariance), but not when using an List
/IList
. Imagine the following scenario:
public class MyClass<T>
{
T GetT() { /* Blah blah */ }
void SetT(T value) { /* Blah Blah */ }
}
Writing this:
MyClass<object> example = new MyClass<string>();
That would work for the first method; example
should return an object
, and MyClass<string>
returns a string, which is legal because of polymorphism.
The second method is more of a problem. Let's say you write this afterwards:
example.SetT(new object());
This is illegal since MyClass<string>
expects a string
but gets an object
. Not good.
You can make this work for interfaces using covariance and contravariance as mentioned before. You could write an interface like this:
public interface Covariant<out T>
{
T FunctionReturningT();
}
public class MyInterfaceImplementation<T> : Covariant<T>
{
public T FunctionReturningT() { /* Blah Blah */ }
}
Making the interface covariant, meaning it's legal to write:
Covariant<object> example = new MyInterfaceImplementation<string>();
Whereas you could also write the following:
public interface Contravariant<in T>
{
void FunctionAskingForT(T value);
}
public class MyInterfaceImplementation<T> : Contravariant<T>
{
public void FunctionAskingForT(T value) { /* Blah Blah */ }
}
You've just made that interface contravariant, meaning it's legal to write this:
Contravariant<string> example = new MyInterfaceImplementation<object>();
Upvotes: 0
Reputation: 49231
It depends if the generic container is set with a covariant or contravariant generic parameter.
You can check the MSDN to see how it's declared.
Upvotes: 0
Reputation: 2177
Because if the list is stored in a List<A>
variable, the program expects to be able to put objects of type A
in that variable. Storing a List<B>
object in a List<A>
variable would prevent you from being able to put objects of type A
in a list which is explicitly declared to be able to hold type A
.
That is:
List<A> b = new List<B>()
// compiler knows list should be of type A, so it expects this to work:
b.add(new A());
But if you could assign a List<B>
to a List<A>
variable, that would produce a type error, even though the compiler knows that the variable b
is of type List<A>
and so should be able to hold a type A
object.
Instead, you just use new List<A>
and add elements of type B
to it, which is allowed, or you change the type of the variable to List<B>
.
Upvotes: 0
Reputation: 61952
You're expection List<T>
to be covariant in T
, but it's not.
In C#, some interfaces are covariant, but classes are not. With A
and B
as above, it is legat to say
IEnumerable<A> b = new List<B>();
or
IReadOnlyList<A> b = new List<B>();
because the interfaces in question are covariant, i.e. declared with "out" as in
public interface IEnumerable<out T> ...
public interface IReadOnlyLies<out T> ...
Upvotes: 0
Reputation: 20722
List<B>
is not assignable to List<A>
for a good reason.
Let's assume that the mismatch is allowed, and let's imagine the following imaginary method in your class C
:
public void DoSomething()
{
b.Add(new A()); // (1)
List<B> tmp = (List<B>)b; // (2)
foreach (B item in tmp) { // (3)
// ...
}
}
b
is typed as a list of A
items, so naturally, we can add a new A
instance to b
.b
is actually an instance of List<B>
, so the cast is valid.b
contains items that are not of type B
. The fact that tmp
is of type List<B>
guarantees that all items of the list are of type B
, but this is not the case any more if assigning List<B>
to List<A>
is allowed.Therefore, the compiler does not allow this mismatch.
Upvotes: 1