Reputation: 155055
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
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
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
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
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
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