【问题标题】:How to override a getter-only property with a setter in C#?如何在 C# 中使用 setter 覆盖 getter-only 属性?
【发布时间】:2014-04-08 06:59:20
【问题描述】:

更新这个问题已经过修改,使其更清晰。下面的答案似乎反映了这种方法效果很好。希望这个问题可以帮助需要将getset 添加到现有属性的人。


今天遇到一个问题,我需要用getset 覆盖基类的get-only 属性。目前的共识似乎是这是不可能的,但我认为我找到了一种方法。

一般的想法是创建一个new 属性而不是直接使用旧的override 属性,然后我们创建一个桥接方法,该方法overrides 旧的get 方法调用新的方法。

情况

下面是一些无法修改的任意预先存在的类型结构的代码。

public abstract class A
{
    public abstract int X { get; }
}
public class B : A
{
    public override int X { get { return 0; } }
}

问题

我们想编写这段代码,但它不会编译。

public class C : B    // won't compile: X can't have a 'set' method
{
    private int _x;
    public override int X { get { return _x; } set { _x = value; } }
}

解决方案

我们还是编写了我们想要的代码,但是我们将属性声明为 new 而不是 override,从而允许我们声明 set 方法。

public class D : C    // Same thing, but will compile because X is 'new'
{
    private int _x;
    public new virtual int X { get { return this._x; } set { this._x = value; } }  // also 'virtual', unless we want to 'sealed' it.

    //  Bridge method provides the override functionality:
    protected sealed override int XGetter { get { return this.X; } }  // 'sealed' because this bridge should always refer to the new 'get' method
}

额外的桥接方法XGetter 提供override。这是使用中间层粘合到基类结构的:

public abstract class C : B  //abstract intermediate layer
{
    //  Override-and-seal the base property's getter.
    public sealed override int X { get { return this.XGetter; }  }

    //  Define the bridge property for the new class to override.
    protected abstract int XGetter { get; }
}

我认为D 现在等同于从B 继承的类,同时还可以在setter 中进行覆盖。这是正确的吗?

【问题讨论】:

  • 实际上这会起作用,但理论上这是不正确的,正确的做法是在抽象类A中添加setter。
  • @Consult Yarla:基类A 具有其他无法设置属性的继承类。如果基类允许设置属性,那么对于不需要它的类,就必须有一个 setter 的虚拟实现。
  • 以前的问题,人们讨论了为什么用 getter/setter 覆盖仅 getter 的属性是不可能的:stackoverflow.com/questions/82437
  • 可以使用类似的方法来覆盖具有派生类型的方法:stackoverflow.com/questions/22313097

标签: c# overriding readonly setter getter


【解决方案1】:

小心您的解决方案,因为它隐藏了 A 和 B 的原始意图。话虽如此,您的解决方案确实有效,即使在转换为基类时也是如此。

例子:

D d = new D();
d.X = 2;
B b = d as B;

Assert.AreEqual(2, b.X);

如果基类可以修改,我推荐使用反射。

【讨论】:

  • 基类没有setter,因为它并不总是有意义的。在我的例子中,基类是Matrix,其中属性在i 行和j 列获取矩阵元素。一些矩阵,比如简单的类似结构的矩阵,可以设置这些元素。其他矩阵,如JacobianMatrix : Matrix,不允许设置单个元素。另外,为什么要推荐反射?
  • 如果您可以修改基类 A,我建议使用反射。如果基本抽象 A 不使用默认的 getter,而是实现了一个字段,则可以通过反射来更改它,而无需所有这些额外的类。
  • 我认为您的示例出现空异常错误,因为 D d = b as D;d 设置为 null,因为 b 不是 D
  • @Nathan R. 我不关注你。 d 不会一直为空吗?
  • 你说得对,我打错了那个。我已经编辑了我的帖子。
【解决方案2】:

通过在您的示例中引入新属性 XGetter,您使解决方案变得比它需要的更复杂。您可以引入相同的属性,只需反转哪个属性获取 getter 和 setter。

public abstract class A
{
    public abstract int X { get; }
}
public class D : A
{
    private int _x;
    public sealed override int X { get { return XGetterSetter; } }
    public virtual int XGetterSetter { get { return this._x; } set { this._x = value; } }
}

上面示例中的 D 类中的代码与原始示例中的代码一样多。这只是消除了您示例中对 B 类和 C 类的需要。

从语义上讲,这个解决方案可能不一样。您必须设置 XGetterSetter 属性而不是 X 属性。 D 的派生类也必须覆盖 XGetterSetter 而不是 X。

【讨论】:

  • 你说得对,如果我们愿意接受新的语义,我们可以省略抽象中间层C,尽管我担心的是(1)打破interfaces,因为它们会受到语义更改的影响,并且 (2) 用看似多余的属性使用户感到困惑。另外,我注意到B 不是“需要的”; AB 都是预先存在的,只是为了说明解决方案(这是 CD 的组合)。
【解决方案3】:

更新: 以下是不正确

没有。

public abstract class A
{
    public abstract int X { get; }

    public int GetXPlusOne()
    {
        return X + 1;
    }
}

您不会更改A.X 的值。

var d = new D();
d.X = 10;

d.GetXPlusOne() == 1

【讨论】:

  • @Keith。我试过了,我认为你错了,它确实有效。
  • 每天学习新东西!谢谢。
猜你喜欢
  • 2018-11-08
  • 1970-01-01
  • 2019-02-28
  • 2011-03-21
  • 2021-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-05
相关资源
最近更新 更多