繁体   English   中英

从派生类隐藏属性

[英]hiding property from derived class

Jon Skeet在他的视频中提到了这个问题(虽然没有提供答案)。

假设我们有一个名为Person的类,而Person类具有Name属性

然后我们有另一个班,间谍。 当然,间谍是一个人,所以我们将从Person类派生。

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

public class Spy : Person
{

}

我们不希望人们知道Spy的名字,所以我们希望这会给出一个编译错误:

static void ReportSpy(Spy spy) {
  string name = spy.Name;
}

或者:

static void ReportSpy(Spy spy)
{
   Person spyAsPerson = spy;
   string name = spyAsPerson.Name;
}

我们怎么能防止这种事情发生呢?

在基本PersonPerson Name属性为virtual 在派生的Spy类中,覆盖该属性并在getter中throw Exception

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

public class Spy : Person
{
    public override string Name
    {
        get
        {
            throw new Exception("You never ask for a spy's name!");
        }
        set
        {
            base.Name = value;
        }
    }
}

但是,我建议不要抛出异常

get
{
    return "**********";
}

因为,它打破了LSP (在另一个答案中提到)。 这意味着什么(只是一个例子),我总是可以这样做

Person x = new Spy();

并将其传递给其他方法,可能就像

void RegisterForNextBallGame(Person p)
{
    playerList.Add(p.Name);
}

这种方法不知道体育场周围的一些间谍漫游,在做一个简单的诚实任务时崩溃!

编辑

为了说清楚,这个name=********** 仍然不是一个正确的解决方案 它只会从异常中拯救! 之后,人们可能会发现许多人在名称为**********的代码中走下来,这将导致后来的惊喜和其他问题。

更好的解决方案是更好的设计。 检查内森的答案以获得一些暗示。

如果作为一个人的一部分透露了你的名字:间谍不是人

让间谍继承人破坏了Liskov替换原则 :一个对象可以用它的子类型替换。

如果Spys没有透露他们的名字,他们就不应该是您设计环境中的人物。 也许您可以设计不同的方式:

public interface IPerson
{
    void EatLunch();
}

public interface INameDisclosingPerson : IPerson
{
    string Name {get; set; }
}

public interface ISpy : IPerson
{
    void DrinkCocktail();
    Package MakeDrop();
}

现实世界中这种糟糕设计的一个例子是NetworkStream 它通过抛出NotSupportedException来实现Position属性。 因此,为Stream编写的代码可能会在运行时为NetworkStream中断。 我也不是这个的粉丝。 一个设计指南:错误的东西应该在编译时破坏,从它们无法实现的接口继承的对象是可怕的。

您可以使用new关键字隐藏基类方法或属性:

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

public class Spy : Person
{
    public new string Name
    {
        get { throw new InvalidOperationException(); }
    }
}

它不会给你编译错误,如果你强制转换,你仍然可以访问基类Name属性。

如果可以修改基类,请将属性更改为虚拟。 然后你可以在derived和class中覆盖它,并且即使使用多态调用也会抛出异常。

所有这些都将在运行时工作,在编译时你无能为力。

正如其他人在答案中提到的那样,这种设计打破了利斯科夫的替代原则,应该避免。

你不能。 如前所述,在访问Spy的Name属性时可能会抛出异常,但这仍然可以编译。 并且,也已经提到过,这将打破Liskov替代原则,并且我想补充一点,即开放封闭原则。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM