首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >为什么不能覆盖仅限getter的属性并添加setter?

为什么不能覆盖仅限getter的属性并添加setter?
EN

Stack Overflow用户
提问于 2008-09-17 12:14:05
回答 16查看 64.2K关注 0票数 151

为什么不允许使用以下C#代码:

public abstract class BaseClass
{
    public abstract int Bar { get;}
}

public class ConcreteClass : BaseClass
{
    public override int Bar
    {
        get { return 0; }
        set {}
    }
}

CS0546“ConcreteClass.Bar.Set”:无法重写,因为“BaseClass.Bar”没有可重写的set访问器

EN

回答 16

Stack Overflow用户

回答已采纳

发布于 2008-09-17 12:17:54

因为Baseclass的编写者已经显式声明Bar必须是只读属性。对于派生来说,打破这个契约并使其成为可读写是没有意义的。

在这个问题上,我和微软站在一起。

假设我是一个新的程序员,他被告知要针对Baseclass派生进行编码。我写了一些东西,假设Bar不能被写入(因为基类显式地声明它是一个get only属性)。现在,使用您的派生,我的代码可能会崩溃。例如:

public class BarProvider
{ BaseClass _source;
  Bar _currentBar;

  public void setSource(BaseClass b)
  {
    _source = b;
    _currentBar = b.Bar;
  }

  public Bar getBar()
  { return _currentBar;  }
}

由于不能按照BaseClass接口设置栏,因此BarProvider假定缓存是安全的-因为栏不能被修改。但是,如果在派生中可以设置,那么如果有人在外部修改了_source对象的Bar属性,这个类可能会提供陈旧的值。重点是“保持开放,避免偷偷摸摸的事情和让人惊讶”。

更新:Ilya Ryzhenkov问道:“为什么界面不遵循同样的规则?”嗯..。随着我的思考,这一点变得更加混乱。

接口是一种约定,它说“期望一个实现具有一个名为Bar的read属性。”我个人认为,如果我看到一个接口,我就不太可能假设它是只读的。当我在接口上看到get-only属性时,我会将其读作“任何实现都会公开此属性栏”……在基类上,它单击为“Bar is a read-only property”。当然,从技术上讲,你并没有违反合同..你做的更多。所以从某种意义上说你是对的..最后,我会说:“尽量避免误解的发生”。

票数 -7
EN

Stack Overflow用户

发布于 2010-04-03 20:06:28

我认为主要原因很简单,因为语法太明确了,不能以其他方式工作。这段代码:

public override int MyProperty { get { ... } set { ... } }

非常明确,getset都是重写。基类中没有set,所以编译器会报错。就像你不能覆盖基类中没有定义的方法一样,你也不能覆盖setter。

您可能会说编译器应该猜测您的意图,并仅将覆盖应用于可以覆盖的方法(即本例中的getter ),但这违反了C#设计原则之一-编译器不能猜测您的意图,因为它可能在您不知道的情况下猜测错误。

我认为下面的语法可能会做得很好,但正如Eric Lippert一直说的那样,实现这样一个次要的功能仍然需要大量的努力……

public int MyProperty
{
    override get { ... } // not valid C#
    set { ... }
}

或者,对于自动实现的属性,

public int MyProperty { override get; set; } // not valid C#
票数 43
EN

Stack Overflow用户

发布于 2014-03-06 06:07:39

这是可能的。

tl;dr–如果需要,您可以使用setter重写get-only方法。它基本上就是:

  1. 创建一个new属性,该属性具有使用相同名称的getset

对新get.的别名执行

  1. override操作之前的get

这使我们能够使用get/set覆盖属性,即使它们的基本定义中缺少设置器。

情况:预先存在的get-only属性。

你有一些不能修改的类结构。也许它只是一个类,或者它是一个预先存在的继承树。不管是哪种情况,您都想将set方法添加到属性中,但做不到。

public abstract class A                     // Pre-existing class; can't modify
{
    public abstract int X { get; }          // You want a setter, but can't add it.
}
public class B : A                          // Pre-existing class; can't modify
{
    public override int X { get { return 0; } }
}

问题:无法使用get/setget-only执行override操作。

您希望使用get/set属性执行override,但它不能编译。

public class C : B
{
    private int _x;
    public override int X
    {
        get { return _x; }
        set { _x = value; }   //  Won't compile
    }
}

解决方案:使用abstract中间层。

虽然不能直接使用get/set属性进行override,但可以

  1. 创建同名的new get/set属性。

使用新的

  1. override方法的访问器对旧的get方法执行
    1. override操作,以确保consistency.

因此,首先要编写abstract中间层:

public abstract class C : B
{
    //  Seal off the old getter.  From now on, its only job
    //  is to alias the new getter in the base classes.
    public sealed override int X { get { return this.XGetter; }  }
    protected abstract int XGetter { get; }
}

然后,编写不能在前面编译的类。这次将编译它,因为您实际上并没有对get-only属性执行override操作;相反,您使用new关键字替换了它。

public class D : C
{
    private int _x;
    public new virtual int X
    {
        get { return this._x; }
        set { this._x = value; }
    }

    //  Ensure base classes (A,B,C) use the new get method.
    protected sealed override int XGetter { get { return this.X; } }
}

结果:一切正常!

var d = new D();

var a = d as A;
var b = d as B;
var c = d as C;

Print(a.X);      // Prints "0", the default value of an int.
Print(b.X);      // Prints "0", the default value of an int.
Print(c.X);      // Prints "0", the default value of an int.
Print(d.X);      // Prints "0", the default value of an int.

// a.X = 7;      // Won't compile: A.X doesn't have a setter.
// b.X = 7;      // Won't compile: B.X doesn't have a setter.
// c.X = 7;      // Won't compile: C.X doesn't have a setter.
d.X = 7;         // Compiles, because D.X does have a setter.

Print(a.X);      // Prints "7", because 7 was set through D.X.
Print(b.X);      // Prints "7", because 7 was set through D.X.
Print(c.X);      // Prints "7", because 7 was set through D.X.
Print(d.X);      // Prints "7", because 7 was set through D.X.

讨论。

此方法允许您向get-only属性添加set方法。你也可以用它来做一些事情,比如:

  1. 将任何属性更改为get-only、set-only或get--set属性,而不管它在基类中是什么。

  1. 更改派生类中方法的返回类型。

主要的缺点是有更多的代码要做,并且在继承树中有额外的abstract class。对于接受参数的构造函数来说,这可能有点麻烦,因为这些参数必须复制/粘贴到中间层中。

额外的好处:你可以改变属性的返回类型。

作为额外的好处,您还可以根据需要更改返回类型。

  • 如果基定义是get-only,则可以使用派生程度更高的返回类型。

  • 如果基定义是set-only,则可以使用派生较少的返回类型。

  • 如果基定义已经是get/set,则:

- you can use a more-derived return type _**if**_ you make it `set`-only;
- you can use a less-derived return type _**if**_ you make it `get`-only.

在所有情况下,如果您愿意,都可以保持相同的返回类型。

票数 26
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/82437

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档