C# Generics: Modify hidden inherited member in derived class

Say I have the following class structure:

public class A {
    public string Name { get; set; }
}

public class B : A {
    public new string Name {
        get { return base.Name; }
        set { base.Name = value + " set in B"; }
    }
}

public static class Test {
    public static void SetAndPrintName<T>(T value, string name) where T : A {
        value.Name = name;

        Console.WriteLine(value.Name);
    }
}

Here's the result I would expect from running the following code:

Test.SetAndPrintName(new A(), "a");
Test.SetAndPrintName(new B(), "b");
----
a
b set in B

Instead, I get:

a
b

Since that didn't work, I tried casting value in SetAndPrintName, which is ugly but worked as expected:

public static void SetAndPrintName<T>(T value, string name) where T : A {
    if (value is B) {
        ((B)(object)value).Name = name;
    } else {
        value.Name = name;
    }

    Console.WriteLine(value.Name);
}
----
Test.SetAndPrintName(new A(), "a");
Test.SetAndPrintName(new B(), "b");
----
a
b set in B

I also tried making the Name property virtual/overridden and that worked too:

public class A {
    public virtual string Name { get; set; }
}

public class B : A {
    public override string Name {
        get { return base.Name; }
        set { base.Name = value + " set in B"; }
    }
}
----
Test.SetAndPrintName(new A(), "a");
Test.SetAndPrintName(new B(), "b");
----
a
b set in B

The question is: why doesn't new work like the others? The generic method knows that value is of type B, so why does C# treat it as an A in one case out of three?

Jon Skeet
people
quotationmark

The generic method knows that value is of type B

Not at compile-time it doesn't. When the compiler sees value.Name, it has to resolve that to one member... and the only member available to it is the one declared in A.

Think of your two properties as being entirely separate - as if they had different names. Let's call them NameA and NameB.

Your code is effectively:

public static void SetAndPrintName<T>(T value, string name) where T : A {
    value.NameA = name;
    Console.WriteLine(value.NameA);
}

That's valid, because the compiler can resolve NameA against A, and T is constrained to be an A.

If you try to explicitly use NameB, however:

// Invalid
public static void SetAndPrintName<T>(T value, string name) where T : A {
    value.NameB = name;
    Console.WriteLine(value.NameB);
}

... that won't work, because the compiler can't resolve NameB for T.

When you use a virtual property, then you've got a single declared member which has its implementation overridden in B... which is precisely what you normally do when you want to modify behaviour.

If you want to use different declared members based on execution-time type (without knowing it beforehand) you could use dynamic:

public static void SetAndPrintName(dynamic value, string name) {
    value.Name = name;
    Console.WriteLine(value.Name);
}

people

See more on this question at Stackoverflow