views:

2429

answers:

9

Hi, I've got a problem with inheritance and generics. This is the code that illustrates my problem:

namespace TestApplication
{
    public class MyClass<T>
    {
        private T field;

        public MyClass(T field)
        {
            this.field = field;
        }
    }

    public class MyIntClass : MyClass<int>
    {
        public MyIntClass(int field)
            : base(field)
        {
        }
    }
}

And when I try to do something like this:

MyClass<int> sth = new MyClass<int>(10);
MyIntClass intsth = (MyIntClass) sth;

I receive cast exception: Invalid cast exception. Unable to cast 'TestApplication.MyClass`1[System.Int32]' to 'TestApplication.MyIntClass'.

What is more I cannot create cast operator:

public static implicit operator MyIntClass(MyClass<int> myClass)

because: 'TestApplication.MyIntClass.implicit operator TestApplication.MyIntClass(TestApplication.MyClass)': user-defined conversions to or from a base class are not allowed

I need to create casts as described above. I don't know why I cannot cast from a type that is the base class. How can I solve this problem? Thanks in advance.

Edit

Thanks for Your answers. Now I see that i cannot convert from a base class to derived class and i see that it doesn't have anything to do with generics. But why i cannot create user-defined conversions from a base class? I have a method that returns the base class. I am able to define a conversion method but creating a cast operator imho would be a better solution.

+11  A: 

You can only cast from a base class to a derived class if the object is actually of type derived class. I mean, you can't cast an instance of base (MyClass<int>) to MyIntClass. You can, however cast it if it was actually of type MyIntClass stored as an MyClass<int> instance.

MyClass<int> foo = new MyIntClass();
MyIntClass bar = (MyIntClass)foo; // this works.

Assume:

class Base {
   int x;
}

class Derived : Base {
   int y;
}

Base foo = new Base();
Derived bar = (Derived)foo;

if it was allowed, what would the value of bar.y be? In fact, converting from Derived to Base is not a conversion at all. It's just telling the compiler to let the variable of type Base to point to an object of type Derived. It is possible since derived has more or equal features than Base which is not the case in the other way around.

If you were able to create a conversion operator between base and derived classes, the C# compiler would be unable to distinguish it from the built in relationships defined for them. This is why you cannot create cast operators along inheritance hierarchies.

Mehrdad Afshari
+1. Also I find a good way of thinking about it is that .NET doesn't know what extra features you may have added to MyIntClass, but it does know that MyIntClass can always be cast back to it's base class. So only one way of casting is available.
John_
Yeah, assume you have a Fruit base class and Apple and Banana subclasses. If a Fruit variable is of type Apple, how we expect it to be cast to Banana?
Mehrdad Afshari
In C# 4.0, you can finally{} compare Apples with Pears, if you only try{};)
devio
But why conversion from a base class is not allowed?
empi
A: 

As Mehrdad stated, you cannot downcast an object. Upcasting is implicit, therefore you cannot overwrite it.

As for the implicit operator, you can still create a constructor in the derived class which receives a parameter of type baseclass.

If you need to cast freely, define the variable as baseclass, but instantiate derived classes.

devio
+9  A: 

The other answers so far are correct, but I'd like to point out that your example has nothing to do with generics. It's the equivalent of:

using System;

class Base {}
class Child : Base {}

class Test
{
    static void Main()
    {
        Base b = new Base();

        // This will throw an exception
        Child c = (Child) b; 
    }
}
Jon Skeet
A: 

As has been said, you're trying to cast an object into a type that it doesn't derive from. Did you perhaps want to do this:

MyClass<int> sth = new MyIntClass(10);
MyIntClass intsth = (MyIntClass) sth;
Mike Scott
A: 

Instead of creating an MyIntClass, try an alias:

using MyClass<int> = What.Ever.Namespace.MyIntClass;

This is now valid:

MyClass<int> foo = new MyClass<int>();
MyIntClass bar = (MyIntClass)foo;

Just understand that when doing the using alias, you have to qualify your namespace on the alias type name (What.Ever.Namespace).

Will
If you need to add methods to your MyIntClass, you can do this via extension methods....
Will
+2  A: 

In the comments you asked:

But why conversion from a base class is not allowed?

Simple - it would make no sense. Consider the example:

class BaseClass
{
    public int x;

    public BaseClass(int StartX)
    {
        this.x = StartX;
    }
}
class ChildClass: BaseClass
{
    public int Y;

    public BaseClass(int StartX, StartY): base(StartX)
    {
        this.y = StartY;
    }
}

class Program
{
    public static void Main()
    {
        BaseClass B = new BaseClass(3);
        ChildClass C = (ChildClass)B;
        Console.WriteLine(C.y);
    }
}

What do you suppose this program would output, assuming the cast worked? Even worse - imagine that BaseClass has two child classes - ChildClassA and ChildClassB. Do you want this to work?

ChildClassA A = new ChildClassA(); BaseClass bc = (BaseClass)A; ChildClassB B = (ChildClassB)bc;

This would effectively allow to cast ChildClassA instances to ChildClassB - completely wrong.

Vilx-
I dont want default cast operator. I'm asking why I cannot create my own cast operator.
empi
A: 

Regarding your second question:

But why i cannot create user-defined conversions from a base class?

Well, suppose you have this scenario

class Base {
}

class Derived {
    public static operator Derived(Base b) { ... }
}

and you tried to do this

Base x = new Derived();
Derived y = (Derived)x;

should the conversion be called? Of course not! The value inside x is actually of type Derived, so the cast is direct, without conversion. But if the value was not of type Derived, but a concrete Base, then the user-defined conversion has to happen because otherwise we'd have a compiler error. This all makes no sense; user-defined conversions are found in compile-time, and the type of the value of x is only known in runtime. Therefore, the compiler would not know what to do - call the user-defined conversion or simply cast the value...

Hope this makes a bit of sense to you.

configurator
A: 

Answering to your last edit.

This code does already compile, it only fails at runtime:

MyIntClass intsth = (MyIntClass) sth;

So, the following cast operator would be redundant if left explicit:

public static implicit operator MyIntClass(MyClass myClass)

So, the compiler should prevent you from adding that conversion. I think the error might be confusing, but I think it just forbids converting class B to class A if B is derived from A (the warning seemed to me to prevent any conversion to A, at first).

If the operator is made implicit, it is also dangerous, because a downcasting can always fail, so you have to:

  1. show the compiler that you know that, by adding an explicit cast;
  2. show the reader (which includes yourself, minutes later) that the operation might fail.
Blaisorblade
A: 

Assignment/conversion of a base class to a derived class makes sense if you consider assignment or conversion to be a value by value copy. What's confusing about c# for newbies is the inconsistent way it does things:

'int' is a 'simple' type: int i = 5; <- this creates an int. int j = i; <- this creates another int and copies the value of i into j.

'Object' is not a simple type: Object a; <- this does not create a copy of 'Object', only a reference to one Object b = new Object(); <- this actually creates an Object a = b; <- this sets the reference to an object a to the reference to an object b. both of them reference the same Object. No values were copied.

If it were doing a copy of values then copying to base class to a derived class would work. C# doesn't work like other languages.

I think that might be what's confusing you.

Jay