Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Polymorphism in generic type parameters

I am trying to use polymorphism in generic type parameters in C#. I have reviewed several other questions on SO (1, 2, 3, 4, 5, 6), but I'm still not clear why this doesn't work (or if it's even allowed).

Background

I have the following classes:

public class Base { }

public class Derived<T> : Base { }

public class Foo { }

and an instance of my derived generic type:

var derived = new Derived<Foo>();

The basics

The following statements are all true:

derived is Object

derived is Base

derived is Derived<Foo>

The problem

When I try to use my derived class as a type parameter in another generic type I get some unexpected behavior. Given the following lazy instance:

var lazy = new Lazy<Derived<Foo>>();

The following is true:

lazy is Lazy<Derived<Foo>>

But these are false when I expected them to be true:

lazy is Lazy<Object>

lazy is Lazy<Base>

Why is this? Should they be true or have I misunderstood how generics work?

like image 317
Kevin Kuszyk Avatar asked Jul 26 '17 09:07

Kevin Kuszyk


People also ask

Can I use polymorphism in generics?

The polymorphism applies only to the 'base' type (type of the collection class) and NOT to the generics type.

What type of polymorphism are generics?

Parametrically polymorphic functions and data types are sometimes called generic functions and generic datatypes, respectively, and they form the basis of generic programming.

What do you mean by parametric polymorphism?

Parametric polymorphism occurs when a routine, type or class definition is parameterized by one or more types. It allows the actual parameter type to be selected by the user. This way, it is possible to define types or functions that are generics, which can be expressed by using type variables for the parameter type.

Can a parameter to a method be polymorphic?

Can a parameter to a method i.e. "catch(Animal a)" be polymorphic? A method can accept a polymorphic reference; this may give the method more flexibility than it would otherwise have.


1 Answers

Yes, you misunderstood how generic works. This is as well the biggest limitation to usage of Generic types (in fact you should avoid them as much as possible because of that). If Derived inherits from Base then it is normally not true that Generic<Derived> is Generic<Base>. The exception to this is covariance and contravariance. In C# it works only with interfaces and delegate types. If you define your Generic interface like:

public interface Generic<out T> {}

then Generic<Derived> is Generic<Base>

If you define your Generic class like:

public interface Generic<in T> {}

then Generic<Base> is Generic<Derived> (surprise, huh?).

Why the simple cast does not work? Imagine object of a class implementing interface that looks as follows:

public interface Generic<T> 
{
    public void Func1(T input);
    public T Func2();
}

Imagine we have Generic<Derived> object and we are using it as Generic<Base>. In this case Func2 works perfectly - it returns Derived object which can be caster to Base. But Func1 won't work - we have a function that accepts Base object but the actual object has Func1 that accepts only Derived objects and not all Base objects are Derived, right?

This example explains why with in and out inheritance works. If we apply in constraint on type parameter in generic class we commit that T type may only be returned from properties or functions, but it may never be accepted as parameter. In such case our Generic interface looks like this:

public class Generic<out T> 
{
    public T Func2();
}

As we exaplained in previously Func2 will work fine if we will use Generic<Derived> object as Generic<Base>. For the same reason for an interface:

public interface Generic<in T> 
{
    public void Func1(T input);
}

Func1 will work fine if object Generic<Base> will be used as Generic<Derived> - in this case we will always pass to Func1 Derived objects as parameters and Dervied is always Base by definition.

like image 70
mr100 Avatar answered Sep 27 '22 21:09

mr100