是否可以完全覆盖c#中的属性?

问题描述 投票:0回答:2

我有一个来自我无法控制的库的父类,并且我正在尝试创建一个覆盖该类中的公共属性的子类。我发现答案是使用

new
关键字,但我对此行为感到困惑。这是一个例子:

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}");
}

这会导致:

System.Exception: a b no

但我想要:

System.Exception: a b c

我调用的代码接受

A
,因此我将其传递给它一个具有自定义属性 getter 的子类
B
的实例,但库代码仍在使用
A
中的 getter,这会导致不正确的行为。

这是不可能的吗?我用方法而不是属性尝试了同样的事情,但它也不起作用。我很困惑;-;

c# oop inheritance polymorphism
2个回答
1
投票

如果您发现自己在这种情况下使用

new
关键字,则意味着您正在隐藏而不是覆盖该属性。为了使用
override
关键字,您需要将您的属性声明为
virtual

您还需要创建属性设置器

protected
而不是
private
,以便派生类可以访问它们:

public class A
{
    public virtual string Q { get; protected set; }

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

public class B : A
{
    public override string Q { get; protected set; }

    public B(string q) : base("no")
    {
        Q = q;
    }
}

进行这些更改后,输出将是所需的

System.Exception: a b c


1
投票

new
的要点是,它仅隐藏被覆盖的成员。该成员仍然以完全相同的名称存在,因此当您通过基类引用访问实例时,将调用基类成员。

更简单的思考方式是,

new
只是引入了一个全新的成员,例如:

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

现在,MyString-keyowrd 不再覆盖

 
new
,而是创建第二个属性,该属性与
MyString
类中的
A
没有任何关系。为了更好地理解,我们在这里给它起了另一个名字:

class B : A
{
    public string AnotherString { get; set; }
}

因此,当您有

A
类引用时,编译器对
AnotherString
没有任何线索,因此永远不会调用它。

© www.soinside.com 2019 - 2024. All rights reserved.