Search code examples
c#oopinheritancepolymorphism

is it possible to fully override a property in c#?


i have a parent class coming from a library i don't control, and i'm trying to create a child class that overrides a public property in that class. i found that the answer is to use the new keyword, but i'm confused about the behavior. here is an example:

public class A {
    public string Q { get; private set; }

    public A(string q) {
        Q = q;
    }
}

public class B : A {
    public new string Q { get; private set; }
    
    public B(string q) : base("no") {
        Q = q;
    }
}
...
[Fact]
public void Test() {
    var a = new A("a");
    var b = new B("b");
    A c = new B("c");

    throw new Exception($"{a.Q} {b.Q} {c.Q}");
}

this results in:

System.Exception: a b no

but i want:

System.Exception: a b c

the code i'm calling accepts an A, so i pass it an instance of my child class B that has a customized property getter, but the library code is still using the getter from A, which results in incorrect behavior.

is this just not possible? i tried the same thing with methods instead of properties and it also didn't work. i'm so confused ;-;


Solution

  • the point of new is, that it only hides the overriden member. That member still exists with the exact same name, so when you're accessing your instance via a base-class reference, the base-class-member is called.

    An easier way of thinking is, that new just introduces a completely new member, e.g.:

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

    Now instead of overriding MyString, the new-keyword just creates a second property, which has no relation to MyString from class A. So what the compiler more or less does, is creating a second member (e.g. AnotherString) in your derived class with another name and replace all occurences of B.MyString in the codebase by that new property:

    class B : A
    {
        // actually this property has the same name, however it has nothing to do with MyString from class A
        public string AnotherString { get; set; } 
    }
    

    Now all calls to B.MyString are replaced by B.AnotherString. However your library surely has no clue B even exists. There will allways ever be only refereces to your base-class. So the compiler won't replace these properties. Effectivly you have two properties, not just one.

    This is just a simplification, the compiler won't really rename your property, but it's easier to get the idea.

    So when your library-provider didn't implement any means of extensibility - which is by making the member abstract or virtual, there is no way of extending it.