在子类中部分重写虚拟自动属性

24

我遇到了一个理论问题。

以下代码是有效的且可以编译:

public class Parent
{
    public virtual object TestProperty { get; set; }
}

public class Child : Parent
{
    private string _testValue = "Hello World!";

    public override object TestProperty
    {
        get { return _testValue; }
    }
}

public class Consumer
{
    Parent p = new Child();

    public Consumer(){ p.TestProperty = 3; }
}

我的问题是:

为什么C#允许我在子类中部分重写TestProperty自动属性,即使这会导致部分不可预测的行为?这有实际应用吗?

尽管值无法从公共接口获取,但我仍然可以使用父类的setter来设置TestProperty的值(我已经检查了生成的IL代码,setter仍然在父类中设置支撑对象)。


2
我相信有许多类似这样的奇怪事物可以编译并运行,但它们没有实际应用和潜在的丑陋副作用。 锤子可以用来盖房子或砸拇指,这取决于你如何使用它们 :) - Dave Swersky
2
@Dave - 非常正确。我只是想确保,在这种情况下,用锤子砸我的拇指没有什么我看不到的作用。 - Justin Niessner
2个回答

14

这种行为与C#中非自动实现的属性一致。在虚拟属性中仅覆盖get或set方法一直是可能的。因此,如果在自动实现的属性中无法实现这一点,将会产生不必要的不一致性。

例如,以下方式是合法的:

class A
{
    public virtual int P1
    {
        get { return 42; }
        set { }
    }
}

class B : A
{
    public override int P1
    {
        get { return 18; }
    }
}

好的。那么问题并不仅限于自动属性。但在实际应用中还是有些模糊。如果我将整个属性声明为一个单元,并且该单元由get和set组成...为什么我要覆盖其中一个,而让另一个具有潜在的混乱行为呢? - Justin Niessner
@Justin,属性的获取和设置部分是基本上不同的方法,因此它们可以独立地被重写,这是有意义的。为什么要这样做呢?也许我想在setter中添加额外的验证或在getter中添加缓存行为,但保留其他默认行为。我相信有很多有效的用例可以这样做。 - JaredPar
1
@JaredPar - 我了解它们在本质上是不同的方法,但从逻辑上讲,它们在我的脑海中是相互关联的。然而,从语言设计的角度来看,为什么不要求同时覆盖两个方法,然后如果你想保留现有的功能,只需调用 return base.TestProperty();?(也许这就是为什么我不适合设计语言的原因) - Justin Niessner
@Justin 我认为你遇到的问题是假设它们在逻辑上是相互关联的。事实上,大多数情况下确实如此。但它们不必如此,即使在原始的两个中,也有可能正确地覆盖其中一个而不是另一个(考虑在派生类型获取覆盖中进行缓存)。强制用户在一个不实际覆盖另一个的情况下同时覆盖两个...嗯,这是错误的 :) - JaredPar
@JaredPar - 就是这样。你曾经问过一个问题,然后在有人给你答案之后意识到你早就知道答案,但因为被卡在“什么!?”部分而阻塞了吗?谢谢! - Justin Niessner
@Justin,这种情况我也遇到过很多次 ;) - JaredPar

1

但是对于 setter 来说,这难道不是有意义的吗?如果你只部分地覆盖了 setter,那么你可以在不必费心重写 getter 的情况下响应该事件,并调用 base.TestProperty = value


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接