Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why no compiler error when I cast a class to an interface it doesn't implement?

If I try an invalid cast from a class to an interface, then the compiler doesn't complain (the error occurs at runtime); it does complain, however, if I try a similar cast to an abstract class.

class Program
{
    abstract class aBaz
    {
        public abstract int A { get; }
    }

    interface IBar
    {
        int B { get; }
    }

    class Foo
    {
        public int C { get; }
    }

    static void Main()
    {
        Foo foo = new Foo();

        // compiler error, as expected, since Foo doesn't inherit aBaz
        aBaz baz = (aBaz)foo;

        // no compiler error, even though Foo doesn't implement IBar
        IBar bar = (IBar)foo;
    }
}

Why doesn't the compiler reject the cast from Foo to IBar, when it's (seemingly?) invalid? Or, to flip the question, if the compiler allows this "invalid" cast to the interface IBar, why doesn't it allow the similar "invalid" cast to the abstract class aBaz?

like image 592
McGarnagle Avatar asked Sep 09 '12 01:09

McGarnagle


2 Answers

You need to understand the inheritance system of .Net to see why this makes sense. In .Net, a class may inherit from only one base class but may implement any number of interfaces.

class Program
{
    abstract class aBaz
    {
        public abstract int A { get; }
    }

    interface IBar
    {
        int B { get; }
    }

    class Foo
    {
        public int C { get; }
    }

    class BarableFoo : Foo, IBar
    {
        public int C { get; }
    }

    static void Main()
    {
        // This is why the compiler doesn't error on the later cast
        Foo foo = new BarableFoo();

        // compiler error: aBaz is a class and the compiler knows that
        // Foo is not a _subclass_ of aBaz.
        aBaz baz = (aBaz)foo;

        // no compiler error: the class Foo does not implement IBar, however at runtime
        // this instance, "foo", might be a subclass of Foo that _implements_ IBar.
        // This is perfectly valid, and succeeds at runtime.
        IBar bar = (IBar)foo;

        // On the other hand...
        foo = new Foo();

        // This fails at runtime as expected. 
        bar = (IBar)foo;
    }

}

In the extremely simple original example in the question, it seems like the compiler could detect that this instance of foo is never going to be castable to IBar, but that is more of a "nice to have" warning than a matter of language correctness.

like image 123
BitwiseMan Avatar answered Oct 28 '22 03:10

BitwiseMan


The whole point of a cast is to suppress that compiler error.
(eg, if you know that foo is actually an instance of a subtype that does implement the interface)

If the compiler can prove that it is impossible for the cast to succeed, it will still give an error. (eg, if you cast to a class that is not in its hierarchy)

like image 40
SLaks Avatar answered Oct 28 '22 03:10

SLaks