【问题标题】:Overriding properties to make them readonly - what about the setter?覆盖属性以使它们成为只读的 - setter 呢?
【发布时间】:2015-05-20 20:18:54
【问题描述】:

我在类层次结构中有以下设计目标:

有一个BaseClass 定义了一些属性,通常是读/写:

public class Media
{
    public virtual object Content { get; set; }
    public virtual double recordingLength { get; set; }
}

目的是让一些子类现在这个属性是只读的:

public class CompactDisk : Media
{
    public override object Content
    { 
        get { return this.getContent(); }
        set { 
               // THERE SHOULDN'T BE A SETTER 
            }
    }

    public override double recordingLength
    { 
        get { return 74; }
        set { 
               // NO SETTER EITHER HERE!
            }
    }
}

我在这里迷路了,因为我不知道应该如何实现我的设计意图。

【问题讨论】:

  • 你的设计意图本质上是错误的。
  • @heltonbiker 如果不是所有的媒体内容都可以设置,那么Media 不应该有内容的设置器。如果需要,让派生类添加一个setter。
  • @heltonbiker 您应该遵循 hvd 的建议。 OOP 和继承的目的是转换为父类型不应该在操作中引入任何差异。 即在这里,您尝试抑制 Media 类提出的基本要求,而不是您应该使用Media 类来提出基本 要求和子类可以添加更多。您可能会受益于第二个MediaMutableMedia 或类似的,它继承Media 并在这些属性上具有setter
  • @heltonbiker 不是简单地覆盖属性,而是您可以通过重新引入属性(new 关键字)来实现,其中基类只读属性的 getter 和 getter对于派生类的读写属性做同样的事情。

标签: c# inheritance properties polymorphism setter


【解决方案1】:

一种可能的方法是使用接口

您可以将基本概念拆分为两个界面:

public interface IWritableMedia
{
    object Content { set; }
    double recordingLength { set; }
}

public interface IReadOnlyMedia
{
    object Content { get; }
    double recordingLength { get; }
}

然后像CompactDisk 这样的东西应该只实现IReadOnlyMedia

public class CompactDisk : IReadOnlyMedia
{
    public object Content { get { return ......; } }
    public double recordingLength { get { return .......; } }
}

如果你想实现 CD-RW(可重写),你应该实现这两个接口:

public class RewritableCompactDisk : IReadOnlyMedia, IWritableMedia
{
    public object Content { get; set; }
    public double recordingLength { get; set; }
}

这样您可以将变量键入为IReadOnlyMediaIWritableMedia

IReadOnlyMedia media = new CompactDisk();
IWritableMedia media2 = new RewritableCompactDisk();

现在的问题是 IWritableMedia 不提供 getters 并且您不想声明另一个 IReadOnlyMedia 类型的变量。解决方案是设计一个名为IReadWriteMedia 的第三个接口,RewritableCompactDisk 应该实现它:

public interface IReadWriteMedia : IReadOnlyMedia, IWritableMedia
{
}

public class RewritableCompactDisk : IReadWriteMedia
{
    public object Content { get; set; }
    public double recordingLength { get; set; }
}

由于 IReadWriteMedia 实现了 IReadOnlyMedia 和 IWritableMedia,现在您可以使用 IReadWriteMedia 键入变量并访问 getter 和 setter:

IReadWriteMedia media3 = new RewritableCompactDisk();
object content = media3.Content;
media3.Content = "hello world";

【讨论】:

  • 非常非常好!现在谢谢!
  • @heltonbiker 没问题 :)
  • 我接受了另一个答案,因为我认为它解决了我发布的代码示例的实际缺陷,但是您的方法是我肯定会考虑在将来出现此问题的任何时候使用的方法。再次感谢!
  • @heltonbiker 好吧,没问题,我不在这里是因为积分。我喜欢解决问题 :) 不客气
【解决方案2】:

你不能,或者真的不应该,设计一个子类型“隐藏”基本类型的功能。你可以:

  1. 在你的设置器中抛出一个NotSupportedException,或类似的。这就是Stream 类在您尝试设置无法设置的流长度时的行为方式。

  2. 改变你的设计。我看不到让属性按您想要的方式工作的方法(不诉诸“隐藏”,恕我直言这不是一个好的解决方案),但可能是这样的:

    public interface IMedia
    {
        object Content { get; }
        double RecordingLength { get; }
    }
    
    public interface IWritableMedia : IMedia
    {
        void SetContent(object content);
        void SetRecordingLength(double length);
    }
    

您的 CompactDisk 将只实现 IMedia 接口,而 HardDrive 类可能会选择实现 IWritableMedia 接口。

【讨论】:

  • 在 C# 中,setters 不必使用 methods,而是查看属性上的 new 关键字。
  • Here's how you can do it with properties. 从技术上讲这是隐藏的,但对基类属性的调用是完全正常的,并调用派生类实现。
  • 我喜欢这种方法,并想问你一个额外的说明:你完全摆脱了抽象类,而是使用接口。 假设我对@9​​87654330@ 有一些我希望在MediaBase 中具有的共同行为: 1) MediaBase 应该实现IMedia,还是每个子类都应该单独实现它? 2) MediaBase 什么时候应该是抽象的?抱歉打扰了,但我一直对此感到困惑......
  • @EBrown 我认为成员上的new 是一个糟糕的语言功能,不想使用它。如果你愿意,那很好。
  • @heltonbiker 我选择接口是因为它们更易于举例,但你也可以对类进行同样的思考。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-02
  • 2018-11-08
  • 1970-01-01
  • 2017-11-01
相关资源
最近更新 更多