tags:

views:

107

answers:

4

It is quite puzzling to find out that Generics Constraint Can't Be Casted to Its Derived Type.

Let's say I have the following code:

public abstract class BaseClass
{
    public int Version
    { get { return 1; } }

    public string FixString { get; set; }

    public BaseClass()
    {
        FixString = "hello";
    }

    public virtual int GetBaseVersion()
    {
        return Version;
    }
}

public class DeriveClass: BaseClass
{
    public new int Version
    { get { return 2; } }
}

And guess what, this method will return a compilation error:

    public void FreeConversion<T>(T baseClass)
    {
       if(baseClass.GetType()==typeof(DeriveClass)
        var derivedMe = (DeriveClass)baseClass;
    }

I would have to cast the baseClass to object first before I can cast it to DerivedClass, i.e.,

    public void FreeConversion<T>(T baseClass)
    {
       if(baseClass.GetType()==typeof(DeriveClass)
        var derivedMe = (DeriveClass)((object)baseClass);
    }

Seems to me pretty ugly. Why this is so?

+7  A: 

First, you shouldn't be casting a base type variable to a derived type. It's not supposed to work, only the other way around.

Second, why it works via object, is because you remove the compile-time type checks. The compiler can check that a BaseType cannot be cast to DerivedType. But when a variable is object, the compiler leaves it assuming you know what you're doing. Even if it will compile, the code will then crash during execution.

Developer Art
+3  A: 

The answer is simple: the compiler can't know that T in your FreeConversion method can be converted to DeriveClass.

Tomas Lycken
Why is it that the compiler can't know? I thought it should know because I explicitly specify that `DeriveClass` is an inherited class for `BaseClass`
Ngu Soon Hui
@Ngu: No, you don't. You *test* to see *if* `T` is of type `DeriveClass`, and then - *independently of the test* - you try to explicitly cast `T` to `DeriveClass`. Remember, the compiler doesn't *run* your code - it just *compiles*. What you could do, is convert with an `as` clause instead of an explicit cast. `baseClass as DeriveClass` will return the instance as a `DeriveClass` if possible, and `null` otherwise - but if you check that it's possible first (as you do now) you know (even though your code does not) that it will never be `null`.
Tomas Lycken
@Ngu The compiler can't know because nobody can know! When the template is created there is nothing to say that it will only ever receive a BaseClass paramter and certainly nothing to say that it'll be a DerivedClass value. There's nothing to stop someone calling `FreeConversion(42)` for example. The only exception is a member method of a template restrained to be derived from its own parameter (actually useful in some cases).
Jon Hanna
A: 

As you already stated, the cheap trick is to first cast to object, then to the type you want to go. Ugly, but it works.

Apart from that, it may be that you are violating Liskov Substitution principle, nothing that will harm any animals but can drive your design towards unmaintainable code.

Third, a nice trick to let your base class expose the derived type is something like this:

public class Base<T> where T : Base<T> {
  T IAmDerived;
}

public class Derived : Base<Derived> { }
flq
A: 

First of all, in your generic method the type T could be a vale type or reference type. And the reason why it allows you to do via 'Object' is that, you're simply doing boxing-unboxing which works for any type in system.

Secondly.it will be a terrible idea to convert/cast a baseclass object into a derived class. You're violating the mechanics of OOP.

If you really want to return an object of type derived from the base class, here's one way possible - the solution is pretty much similar to what Frank has offered.

//This is how you create a function in BaseClass that returns the collection 
//of DerivedTypes when called from an object of type derivedclass, no magic just Generics.

//**The BaseClass**
public class BaseClass<T>
    where T : BaseClass<T>
{
    public HashSet<T> GetHashSet()
    {
        HashSet<T> _hSet = new HashSet<T>();
        //do some work              
        //create a HashSet<T> and return;              
        return _hSet;
    }
}
//**The Derived Class**
public class DerivedClass : BaseClass<DerivedClass>
{
    //you have the method inherited.
}
this. __curious_geek