Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Casting a generic element type downwards

public class ConfigControlBase<T> : UserControl
    where T : ProviderBase
{
    public T Provider { get; set; }

    public void Init(T provider)
    {
        this.Provider = provider;
    }
}


public abstract class ProviderBase
{
    public abstract ConfigControlBase<ProviderBase> GetControl();
}

public class ProviderXConfigControl : ConfigControlBase<ProviderX>
{
}

public class ProviderX : ProviderBase
{
    public override ConfigControlBase<ProviderBase> GetControl()
    {
        var confControl = new ProviderXConfigControl() as ConfigControlBase<ProviderX>;
        return confControl;
    }
}

return confControl; throws an exception:

Cannot implicitly convert type ConfigControlBase<ProviderX> to ConfigControlBase<ProviderBase>

like image 796
Rookian Avatar asked Feb 07 '11 16:02

Rookian


People also ask

Can you cast generic type Java?

The Java compiler won't let you cast a generic type across its type parameters because the target type, in general, is neither a subtype nor a supertype.

Which character is used for generic type?

The question mark ( ? ) wildcard character can be used to represent an unknown type using generic code. Wildcards can be used with parameters, fields, local variables, and return types.

Can you instantiate an array using a generic type?

Array of generic typesNo, we cannot create an array of generic type objects if you try to do so, a compile time error is generated.

Which of these type Cannot be used to initiate a generic type?

1. Which of these types cannot be used to initiate a generic type? Explanation: None.


2 Answers

Let's change the name of your classes and properties, but keep the shape the same:

public class Cage<T> where T : Animal
{
    public T Contents { get; set; }
}

public class Aquarium : Cage<Fish> { }

public abstract class Animal
{
    public abstract Cage<Animal> GetCage();
}

public class Fish : Animal
{
    public override Cage<Animal> GetCage()
    {
        return (Cage<Animal>)(new Aquarium());
    }
}

Now is it clear why this is not legal? Suppose it were legal. Then you could do this:

Fish fish = new Fish();
Cage<Animal> cage = fish.GetCage();
cage.contents = new Tiger();

And now you have a tiger in your aquarium. And no one wants that.

The compiler (or runtime) has to prevent this type error somehow; it chooses to prevent it as soon as possible. The earliest it can do so is on the type test for the conversion from Aquarium to Cage<Animal>. The compiler knows that this can eventually lead to tigers in aquariums, so it does not allow the conversion at all. If you force the compiler to allow it through casts then it fails at runtime.

like image 154
Eric Lippert Avatar answered Oct 14 '22 08:10

Eric Lippert


Generic types with assignable type arguments are not assignable themselves.
For instance, you cannot cast List<string> to List<object>, although string is an object.

It is not immediately obvious why such casting is not supported so let me give you an example:

var words = new List<string> { "Serve God", "love me", "mend" };
var objects = (List<object>) words; // C# compiler wouldn't allow this
objects.Add (new Car()); // we just added a Car to Shakespeare's work and the universe exploded

C# doesn't encourage universe explosion, however since C# 4.0 a light version of this idea is implemented. You see, in some cases such casting would actually be safe.

.NET 4.0 brings concepts of covariance and contravariance in generics only for interfaces and delegates, you may want to check this out.

Example (doesn't work prior to .NET 4.0):

void HandleCollection (IEnumerable<object> collection)
{
    // ...
}

var words = new List<string> { "Serve God", "love me", "mend" };

// IEnumerable is defined as IEnumerable<out T> in .NET 4.0
// 'out' keyword guarantees that T is only used for return values
// and therefore client code can't explode the universe   

var objects = (IEnumerable<object>) words;
HandleCollection (objects);
like image 8
Dan Abramov Avatar answered Oct 14 '22 10:10

Dan Abramov