1

Lets assume we have two classes A and B in which B derives from A.

class A
{
}

class B : A
{
}

class C<T>
{
}

Now C<B> doesn't derive from C<A>.

This gets me a bit confused, because I tough that "everything" I can do with A, I can do with B. I'm sure that i'm missing something because it seems contradicting to the basics of OOP.

Is there any concrete justification and example for that?

Edit:

I've read many similar posts like: In C#, why can't a List<string> object be stored in a List<object> variable

Why can't I cast a dictionary of one value type to dictionary of another value type when the value types can be cast from one another?

Casting from IEnumerable<Object> to IEnumerable<string>

But in every post the answer is focused in a specific class/data structure, and making this specific code to work, rather then why it's not possible in general?

Community
  • 1
  • 1
Yosi Dahari
  • 6,794
  • 5
  • 24
  • 44
  • You mean `class C`? – Ahmed KRAIEM Oct 17 '13 at 15:58
  • 1
    @AhmedKRAIEM An instance of `C` using `B` as the value of the generic argument... – Servy Oct 17 '13 at 15:58
  • 3
    This is called covariance. It is not safe in general. What if `C` takes a `B` as a parameter? You can't pass an `A` as a `B`. – SLaks Oct 17 '13 at 16:00
  • 1
    This question is asked pretty much every day on SO. Do a search for "covariance" and you will find dozens of answers to your question. – Eric Lippert Oct 17 '13 at 18:06
  • 1
    There's way too many duplicates; it's hard to pick a favorite. And it's also hard to pick the right match from the dozens of choices, running the risk of people rejecting the close. I feel like we need a canonical question. Regardless, it's flagged :). Possible duplicate of [In C#, why can't a List object be stored in a List variable](http://stackoverflow.com/questions/6557/in-c-why-cant-a-liststring-object-be-stored-in-a-listobject-variable) – Brian Oct 17 '13 at 18:21
  • @Yosi: Try reading through [Eric Lippert's answers to Covariant Questions](http://stackoverflow.com/search?q=user%3A88656+%5Bcovariance%5D). One of them is probably a duplicate of yours; I'm not sure which :/ – Brian Oct 17 '13 at 19:02
  • @Yosi You linked to sevearal examples of why it's not possible in specific instances. It would need to be valid in all possible instances for it to be a feature, thus your listing of a single counter example explains exactly why it can't be done. – Servy Oct 17 '13 at 19:04
  • 1
    @Yosi: A canonical question would be an existing (highly ranked) question, or a new question written and answered by someone knowledgeable (and both would probably need to be CW, since they'd be intended for everyone to edit them). However, there are already *way* too many questions on this topic; I'm not sure it's feasible to create or find a decent canonical covariance questions which covers everything. Instead, anyone interested should edit the [contravariance](http://stackoverflow.com/tags/contravariance/info) and [covariance](http://stackoverflow.com/tags/covariance/info) tag wikis. – Brian Oct 17 '13 at 19:29
  • See [why-doesnt-c-sharp-do-simple-type-inference-on-generics](http://stackoverflow.com/questions/4503603/why-doesnt-c-sharp-do-simple-type-inference-on-generics) and [why-does-csharp-not-allow-co-and-contravariance-in-generic-class-types](http://stackoverflow.com/questions/2541467/why-does-c-sharp-4-0-not-allow-co-and-contravariance-in-generic-class-types) – nawfal Jul 10 '14 at 06:54

3 Answers3

9

I though that "everything" I can do with A, I can do with B. I'm sure that I'm missing something because it seems contradicting to the basics of OOP.

Let's start by stating the basic principle that you are alluding to. From Wikipedia:

If S is a subtype of T, then objects of type T may be replaced with objects of type S without altering any of the desirable properties of that program

What you are missing is the key word: objects.

You can substitute an object of type B anywhere that an object of type A is required. But it is not the case that everything you can do with the type A you can also do with the type B. If someone asks you for a car and you give them a 1995 Ford Escort, they can use that as a car. But that doesn't mean that you can do a search-and-replace in Wikipedia and change every usage of the word "car" to "1995 Ford Escort" and have the articles still be correct!

In fact, the reason that you cannot use C<B> where C<A> is expected is because doing so violates the Liskov Substitution Principle:

class A {}
class B : A {}
class D : A {}
class C<T>
{
  public T M(T t) { ... }
}
...
C<B> cb = new C<B>();
C<A> ca = cb; // Suppose this were legal
ca.M(new D());

You can use an object of type D anywhere you can use an A, but ca is really a cb, and you cannot use a D anywhere you can use a B! Therefore you cannot use a C<B> anywhere you can use a C<A>.

C# 4 and above do allow this kind of covariance when the compiler can prove that doing so is safe. Do a web search on C# covariance for details; you'll find plenty of articles on the subject. (Including mine.)

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
2

Look at this example:

class C<T>
{
    public T Value { get; set; }
}

// ...

C<B> cb = new C<B>();

Now, if C<B> derived from C<A>, the following would be possible:

C<A> ca = cb;
ca.Value = new A(); // ca.Value is defined by C<A> and thus typed to A

Imagine what happens when we access cb.Value now, assuming that the stored instance can only be of type B, yet it actually is of type A.

Therefore, C<B> does not derive from (is not assignment-compatiable to) C<A>.

O. R. Mapper
  • 20,083
  • 9
  • 69
  • 114
  • What if `class C`? – haim770 Oct 17 '13 at 16:07
  • 1
    @haim770 you can't do that with classes, only interfaces. Maybe the OP wants an covariant (`out` parameter) interface, though. – Tim S. Oct 17 '13 at 16:08
  • 4
    @haim770: Beside what Tim said, when using the `out` generic modifier you can only *return* values of that type, not accept any (that's why it's called `out` ...), as otherwise you could assign supertypes again. Likewise, when using `in`, you can only use the type parameter for parameters (i.e. *in*-bound values). In other words, a readable *and* writeable property of the generic type as shown in my example would be impossible when using either `out` or `in` as generic modifiers. – O. R. Mapper Oct 17 '13 at 16:11
1

Well, when you write a class C<T>, it is all about C, not T. T is merely a type info provided to denote a special C<> type. C<> has its own implementation and has no relation at all with T. When you write:

var c = new C<int>();

the behaviour of variable c is all in accordance with what you have written in class C<>. It behaves the same whether T is int or string or anything.

Also note that a class written like C<S> is a special type and different from C<T> which is another type, because all the behaviour is confined by it's C-ness. We never know how is C<> written and how it will behave. So the way you have described is not how generics play out. If you have an additional class like

class D<T> : C<T>
{
}

you can write:

C<A> c = new D<A>();

You see, it is again possible because C derives from D. The left side of the class matters. This is again illegal:

C<A> c = new D<B>();

even though A derives from B, because C of A has no relationship with D of B (or even C of B) which is a separate class.

This kind of generic polymorphism is actually meaningless when you think C<A> as an entirely different type to C<B> though they have a common behaviour. To illustrate with another example, think about this, a class can be generic by more than one type. Suppose there is

class C<S, T>
{
}

What about C<IList<A>, A> and C<IList<int>, B>? Are they type comparable because those two have IList and A and B as generic types? Is C<T> castable from C<S, T>? No these are different types in short.

Now if you wan't to make the assignment

C<A> ca = new C<B>();

possible you can write your own implicit converters.

class C<T>
{
    public static implicit operator C<T>(C<B> c)
    {
        type check, explode or return
    }
}

//now you may write:
C<A> ca = new C<B>();
//or even
C<A> ca = new D<B>();

Mind you this does not imply inheritance relationship, but merely a conversion operation.

And no information is complete on generics without information on covariance and contravariance (which is applicable only for interfaces and delegates). See O. R. Mapper's answer as to the dangers if this was let to happen. The one case where C# lets this is in case of arrays. And sadly that's a broken design. This is possible in C#

object[] o = new string[1];
o[0] = 1; //explosion

I'm tempted to detail out, but anything I do would make it redundant. See this excellent answer by Eric Lippert for a clear info.

Community
  • 1
  • 1
nawfal
  • 70,104
  • 56
  • 326
  • 368