【问题标题】:How to remove set accessor from inherited property with get set如何使用 get set 从继承的属性中删除 set 访问器
【发布时间】:2014-04-15 16:24:00
【问题描述】:

假设我有一个类层次结构,其中 number 在创建 B 的实例时通过构造函数链接设置为默认值。由于 _number 的值应该是默认值,因此必须覆盖属性 Number 以便删除 set 访问器。

abstract Class A
{
    public int Number
    {
        get
        {
            return _number
        }
        set
        {
            _number = value;
        }
     }
 }

Class B : Class A
{
    public int Number
    {
        get
        {
            return _number
        }
     }
 }

【问题讨论】:

  • 我不认为这是可能的,并且有一个很好的理由:它会违反 Liskov 的替换原则并违反基类合同 - 显然类 B 不是A,因为它表现出明显不同的行为。
  • 为什么不能做setter private
  • 这打破了 SOLID 的 Liskov 替换。请看stackoverflow.com/questions/22838199/…

标签: c# inheritance properties


【解决方案1】:

这是不可能的,因为你想要并且有充分的理由。

您实际上违反了 Liskov 的替代原则。利斯科夫的说

程序中的对象应该可以替换为它们的实例 子类型而不改变该程序的正确性

由于您的基类有一个公共集,因此您的派生类也应该如此。

我在Override Interface Method in another Interface 中提出的几乎相同的论点可以在这里应用

既然您可以在需要基类的地方传入派生类,那么如果用户调用该属性上的设置,现在会发生什么情况?

虽然新方法看起来像是一种可能的解决方案,但这实际上是错误的,因为新方法隐藏了基本属性并且不会覆盖它。它仍然可以访问,因为您没有隐藏 set 部分(您只是隐藏了 getter)。

如果您在基类中将 setter 设为私有,则无论基类还是派生类,setter 都不会公开可见。但是,如果您按照指定的方式使用构造函数赋值,这可能是一个选项。

【讨论】:

    【解决方案2】:
    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                B b = new B(10);
    
                // b.Number = 10; // Error here
            }
        }
    
        public abstract class A
        {
            public A() { }
    
            public A(int number)
            {
                Number = number;
            }
    
            private int _number;
    
            public virtual int Number
            {
                get
                {
                    return _number;
                }
                private set
                {
                    _number = value;
                }
            }
        }
    
        public class B : A
        {
            public B(int number)
                : base(number) { }
    
            public override int Number
            {
                get
                {
                    return base.Number;
                }
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      可能是这样的

      class B : A
      {
          public new int Number //NEW
          {
              get
              {
                  return _number; //NUMBER IS DEFINED IN (A)
              }
           }
       }
      

      但这有一个问题。

      即使这工作正常

      B b = new B(); 
      b.Number = 10; //COMPILER ERROR FOR READONLY PROPERTY 
      

      这将正确编译

      A b = new B(); 
      b.Number = 10; //NO ERROR
      

      【讨论】:

      • 您应该用粗体字指出这不是覆盖属性而是隐藏属性。
      • 这是一个危险的答案。由于 getter 现在引用派生类,而 setter 引用基类。因此,如果我认为我现在正在修改派生中的 _number2,我仍然会在基础中设置 _number。
      • @Murdock:但你只有一个 _number,在 base 中。也就是说。如果您的程序架构没有明确说明,则不需要任何重复。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-09-15
      • 2016-10-06
      • 2012-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多