Casting a universal type down

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>

+7
source share
5 answers

Let me change the name of your classes and properties, but keep the form 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 it’s clear why this is not legal? Suppose this was legal. Then you can do this:

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

And now you have a tiger in your tank. And nobody wants that.

The compiler (or runtime) must somehow prevent this type error; he decides to prevent it as soon as possible. The earliest thing he can do is type test to convert from Aquarium to Cage<Animal> . The compiler knows that this can ultimately lead to tigers in aquariums, so it does not allow conversion at all. If you force the compiler to allow it using drives, then it does not work at run time.

+21
source

Generic types with assignable type arguments cannot be assigned themselves.
For example, you cannot use List<string> for List<object> , although string is an object .

It’s not immediately clear 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 work and the universe exploded 

C # does not encourage an explosion in the Universe, however, with C # 4.0 a light version of this idea is implemented. You see, in some cases, such a cast will be really safe.

.NET 4.0 provides the concepts of covariance and contravariance in generics only for interfaces and delegates , you can check this out.

Example (does not work before .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); 
+8
source

This is because ConfigControlBase<ProviderX> not ConfigControlBase<ProviderBase>

+1
source

your

  public override ConfigControlBase<ProviderBase> GetControl() 

does not match

 var confControl = new ProviderXConfigControl() as ConfigControlBase<ProviderX>; 
0
source

This answer may not be acceptable in your scenario, since you should probably look for a different solution, but during reflection I found it possible to use less typical types, very useful , so I wrote a solution for this. However, it only works for interfaces, and you must ensure that you pass only objects of the correct types to the interface.

I basically create a proxy class at runtime that does all the tasks you need. It looks like this:

 object validator; // An object known to implement IValidation<T>. object toValidate; // The object which can be validated by using the validator. // Assume validator is IValidation<string> and toValidate a string. IValidation<object> validation = Proxy.CreateGenericInterfaceWrapper<IValidation<object>>( validator ); validation.IsValid( toValidate ); // This works! No need to know about the type. // The following will throw an InvalidCastException. //validation.IsValid( 10 ); 

More information and source code can be found on my blog .

0
source

All Articles