Alin
Alin

Reputation: 394

Upcasting derived class to base class in C#

I am struggling to understand this scenario with casting. For demo purposes I created these classes:

public interface IFoo {}
public class Foo : IFoo
{
      public string Name { get; set; }
}
public class Bar : Foo
{
      public string Surname { get; set; }
}

Now, in my Main method I created a static method which returns IFoo:

class Program 
{
    static void Main()
    {
        IFoo iFoo = GetIFoo();
        Foo foo = (Foo)iFoo;           
       //the GetType ext method is executed and it returns Bar
       if(foo is Foo)
       {
            Console.WriteLine($"foo is of type: {foo.GetType()}");
       }
       Console.ReadLine();
    }
    static IFoo GetIFoo()
    {
         var bar = new Bar
         {
             Name = "MyName",
             Surname = "MySurname"
         }    
         return bar;
    }
}

The question is: even though I'm creating a Bar in the GetIFoo method why is it that if I'm casting the IFoo to Foo, foo's type is Bar and not Foo?

Upvotes: 1

Views: 1098

Answers (6)

Ondrej Tucny
Ondrej Tucny

Reputation: 27962

This is really inheritance 101: The object created is a Bar and casting just affects how you view it, not what it truly is.

Here you view it as an IFoo:

IFoo iFoo = GetIFoo();

And here you deliberately view it as a Foo. It it were not a Foo, or any of its descendants, you'd get a InvalidCastException on run-time.

Foo foo = (Foo)iFoo;

A Bar instance can legally be worked with as Bar, Foo, and IFoo (and as an Object for the sake of completeness). Regardless how you view the instance, it remains a Bar.

Upvotes: 1

Vikhram
Vikhram

Reputation: 4394

You are confused between the runtime-type of an object and its compile-time type.

IFoo iFoo = GetIFoo();

In the above line, iFoo's runtime-type is still Bar. This is the real type of the object. But if you access the Bar object via a reference of type IFoo then the compiler only knows that the type is IFoo, hence called the compile-time type of the object pointed by reference iFoo. Note that the compiler lost this type information when the return bar statement in GetIFoo was executed.

Foo foo = (Foo)iFoo;

Further down, you can cast it to whatever type you want in the hierarchy of Bar, anything above and including Bar will succeed, and the compile time type will be based on what you type-casted it to. But again the runtime type will still stay the same i.e Bar

Upvotes: 1

George Alexandria
George Alexandria

Reputation: 2936

Because GetType() return type of object. When you explicity cast derived class to base class you didn't create a new instance, you only have new typed reference on existing object. You can see it with code below:

IFoo iFoo = GetIFoo();
Foo foo = (Foo)iFoo;
Console.WriteLine(ifoo.Equals(foo));

Upvotes: 1

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476537

Because with the line:

Foo foo = (Foo) iFoo;

you do not convert: a Bar is a Foo and an IFoo all at the same time. So when you cast to (Foo) the C# interpreter notices that iFoo is a Bar and thus leaves the object untouched. The point is, that from now on, the compiler will assume that foo is indeed a Foo object, but it can be of any class that extends the Foo class.

Let's make it more clear with the following example:

interface IDriveable {}  // IFoo
class Car : IDriveable { // Foo
    string Name {get; set; }
}
class Volkswagen : Car { // Bar
    bool CorrectPolution { get; set; }
}

Now if you construct a Volkswagen, ten obviously that is a Car and an IDriveable. But by saying: Car car = (Car) volkswagen;. You do not alter the volkswagen, you only now let the compiler know that car is a Car object.

Upvotes: 1

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726479

why is it that if I'm casting the iFoo to Foo, foo's type is Bar and not Foo?

This is because casting changes the static type. It has no effect on the dynamic type. foo's static type is Foo, but its dynamic type is Bar.

Each variable has a type known to the compiler (static type) and a type known to the runtime (dynamic type). In some cases these two types are the same, but they do not have to be the same all the time.

Static type is the declared type of the variable. Dynamic type is what you get by calling GetType after an object is assigned to that variable.

You constructed an example when a static type of a variable does not match its dynamic type: iFoo has static type of IFoo, but its dynamic type is Bar. This is perfectly fine, because Bar is compatible with IFoo, because it implements the interface, and also with Foo, because it extends the class.

Upvotes: 3

adjan
adjan

Reputation: 13652

Type.GetType gives you the type of the underlying instance (i.e. the type you specify after new), no matter what type of variable holds it. Also, foo is Foo gives true not only if the actual instance is Foo, but for every type derived from Foo.

Upvotes: 1

Related Questions