huysentruitw
huysentruitw

Reputation: 28151

Interface inheritance and property hiding trouble

I recently introduced an interface on a base class for unit-testing purposes and ran into a weird problem. This is the minimal reproducible scenario:

interface IBase
{
    string Text { get; }
}

interface IChild : IBase
{
}

class Base : IBase
{
    public string Text { get { return "Base"; }}
}

class Child : Base, IChild
{
    public new string Text { get { return "Child"; }}
}

static void Main(string[] args)
{
    var child = new Child();
    Console.WriteLine(child.Text); // Outputs "Child"
    Console.WriteLine((child as Base).Text); // Outputs "Base"
    Console.WriteLine(((child as Base) as IBase).Text); // Outputs "Child"
}

The output of the first two Console.WriteLine commands is logical, but I fail to accept the output of the last one, it even outputs Child when using a temporary variable of type Base. Can anyone explain what is happening here?

UPDATE

By removing the interface IChild, ((child as Base) as IBase).Text suddenly results in "Base". Which makes me conclude that, as long as Child implements IBase (directly or through interface inheritance) the result will be "Child" instead of "Base".

This can get really tricky when you refactor a method in an other class to take an argument of IBase instead of Base, as it suddenly results in different behavior.

Upvotes: 9

Views: 142

Answers (1)

Kamil Budziewski
Kamil Budziewski

Reputation: 23107

Basically you are casting here:

(child as Base) to Base and you're using Base's Text field. It's clear.

But in here:

(child as Base) as IBase you are casting Child to Base and then to IBase which means you are casting Child to IBase, which means Child's Text will be displayed. You are not changing the of underlying object by using as.

So (child as Base) as IBase here is the same as child as IBase.

EDIT:

Edited question does not change the fact that this answer is correct. As @InBetween said it is just changing the way how implementation of Text property is resolved. So Child class no longer implements IBase directly, so now Base's Text would be used as best match. Basically it is just using first class implementing IBase

Upvotes: 9

Related Questions