Dai
Dai

Reputation: 155055

C# - Why do I need to upcast to Object before downcasting?

In C# I'm in a scenario with the following types provided by the environment:

public interface IFoo {
}

public abstract class Base {
}

public class Derived : Base, IFoo {
}

public class Arbitrary {
    public Base GetBase() { }
}

Here's what I've written in addition to this. Note that I can guarantee in my code that Arbitrary.GetBase() will always return an instance of Derived.

public class Arbitrary2 : Arbitrary {
    public IFoo GetDerived() {
        return (IFoo)base.GetBase();
    }
}

However this code fails with the message "Cannot convert type 'Base' to 'IFoo'".

But if I do this then it works:

public class Arbitrary2 : Arbitrary {
    public IFoo GetDerived() {
        Object baseAsObject = base.GetBase();
        return (IFoo)baseAsObject ;
    }
}

Why is this upcast to Object necessary before I downcast it to IFoo? The two pieces of code are functionally identical, and the latter will reliably crash at runtime if the cast is invalid. I don't understand why the compiler complains.

Upvotes: 3

Views: 904

Answers (6)

Jeppe Stig Nielsen
Jeppe Stig Nielsen

Reputation: 61952

Can't you just write

public class Arbitrary2 : Arbitrary {
    public IFoo GetDerived() {
        return (Derived)this.GetBase();
    }
}

The compiler will see the connection between Derived and Base, so the explicit cast to Derived ought to be OK. Then any Derived is surely an IFoo, so there's no need to cast an additional time (that conversion is implicit).

Don't use the base. keyword. Either say this. (as above) or leave out.

EDIT: Also, your original code did compile, but my version might be a bit easier to read.

Upvotes: 1

Reed Copsey
Reed Copsey

Reputation: 564383

You don't need to do this. Your code should work, as is. See this program for details:

using System;

public interface IFoo { }

public abstract class Base { }

public class Derived : Base, IFoo { }

public class Arbitrary {
    public Base GetBase() { return new Derived(); }
}

public class Arbitrary2 : Arbitrary {
    public IFoo GetDerived() {
        return (IFoo)base.GetBase();
    }
}

class Program
{
    static void Main(string[] args)
    {
        Arbitrary2 test = new Arbitrary2();
        IFoo check = test.GetDerived();

        Console.WriteLine(check.GetType().Name);

        Console.WriteLine("Press key to exit:");
        Console.ReadKey();
    }
}

Upvotes: 6

Omaha
Omaha

Reputation: 2292

Arbitrary.GetBase() returns an instance of Base. Base's hierarchy does not contain IFoo.

At runtime, yes, your object is a Derived instance, but based on what the compiler knows--class relationships--there isn't a connection and therefore a way to cast from Base to IFoo as you are trying in your first method.

Upvotes: 1

dlev
dlev

Reputation: 48596

The reason the compiler disallows the explicit cast is that Base doesn't implement IFoo.

If you can guarantee that GetBase() will always return a Derived, then you can just insert a cast to Derived prior to the IFoo cast:

public class Arbitrary2 : Arbitrary {
    public IFoo GetDerived() {
        return (IFoo)(Derived)base.GetBase();
    }
}

Of course this will throw at run-time if you're mistaken. Alternatively, you can use an as cast will just return null if it fails:

public class Arbitrary2 : Arbitrary {
    public IFoo GetDerived() {
        return base.GetBase() as IFoo;
    }
}

Upvotes: 1

David
David

Reputation: 218828

Base can't be converted to IFoo because Base doesn't have anything to do with IFoo:

public abstract class Base { }

Based on this declaration, there could very well be instances of Base which aren't of type IFoo. Indeed, based on this declaration the compiler has absolutely no reason to assume that any given instance of Base would ever implement IFoo.

Derived implements IFoo:

public class Derived : Base, IFoo { }

But you're not returning a Derived, you're returning a Base. By polymorphing it through Object you're effectively "tricking" the compiler. You're telling it that you know more than it does and it should listen to you. This can be fine, as long as you do in fact know more than the compiler does. And what you know that the compiler doesn't know is that every instance of Base is going to be able to polymorph to IFoo.

In that case, why not just implement IFoo on Base? That way you'd be sharing your knowledge with the compiler and everyone will be happy.

Upvotes: 1

Maess
Maess

Reputation: 4146

Your GetBase() method returns Base, which does not implement IFoo.

Upvotes: 1

Related Questions