我有一个来自我无法控制的库的父类,并且我正在尝试创建一个覆盖该类中的公共属性的子类。我发现答案是使用
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,这会导致不正确的行为。
这是不可能的吗?我用方法而不是属性尝试了同样的事情,但它也不起作用。我很困惑;-;
如果您发现自己在这种情况下使用
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
。
new
的要点是,它仅隐藏被覆盖的成员。该成员仍然以完全相同的名称存在,因此当您通过基类引用访问实例时,将调用基类成员。
更简单的思考方式是,
new
只是引入了一个全新的成员,例如:
class A
{
public string MyString { get; set; }
}
现在,MyString
-keyowrd 不再覆盖
new
,而是创建第二个属性,该属性与 MyString
类中的 A
没有任何关系。为了更好地理解,我们在这里给它起了另一个名字:
class B : A
{
public string AnotherString { get; set; }
}
因此,当您有
A
类引用时,编译器对 AnotherString
没有任何线索,因此永远不会调用它。