为什么不可能覆盖仅getter的属性并添加setter?
我们为什么认为(或者为什么这样)很好,所以Microsoft选择不允许:
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': cannot override because 'BaseClass.Bar' does not have an overridable set accessor
解决方案
回答
因为在IL级别,读/写属性转换为两种方法(getter和setter)。
覆盖时,我们必须继续支持基础接口。如果我们可以添加一个setter,那么我们将有效地添加一个新方法,就类的接口而言,该方法对于外界仍然不可见。
的确,添加新方法本身并不会破坏兼容性,但是由于它将保持隐藏状态,因此决定禁止这样做是很合理的。
回答
因为那样会破坏封装和实现隐藏的概念。请考虑以下情况:创建类并交付它,然后类的使用者使自己能够设置一个属性,而该属性原本只为我们提供一个getter。它会有效地破坏类中可以在实现中依赖的所有不变量。
回答
因为Baseclass的作者已明确声明Bar必须是只读属性。推导打破合同并使其可读写是没有意义的。
我在这方面与Microsoft合作。
假设我是一位新程序员,被告知要针对Baseclass派生进行编码。我写的东西假定Bar不能被写入(因为Baseclass明确声明它是一个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接口设置Bar,因此BarProvider认为缓存是安全的事情,因为Bar不能修改。但是,如果在派生中可以设置set,则如果有人在外部修改_source对象的Bar属性,则该类可能会提供过时的值。关键是"保持开放,避免做偷偷摸摸的事情和令人惊讶的人"
更新:Ilya Ryzhenkov问:"为什么接口不按照相同的规则运行?"
嗯..我想到这变得更加泥泞了。
接口是一个合同,上面写着"期望实现具有名为Bar的读取属性"。就我个人而言,如果看到接口,则不太可能将其假定为只读。当我在接口上看到get-only属性时,我将其读取为"任何实现都会公开此属性Bar"……在它单击的基类上,因为" Bar是只读属性"。当然,从技术上讲,我们并没有违反合同..我们正在做更多的事情。因此,我们在某种意义上是正确的。.最后,我要说:"使误解变得越来越难"。
回答
我们也许可以通过创建一个新属性来解决该问题:
public new int Bar { get { return 0; } set {} } int IBase.Bar { get { return Bar; } }
回答
因为具有只读属性(没有设置器)的类可能有充分的理由。例如,可能没有任何基础数据存储。允许我们创建二传手会破坏班级制定的合同。糟糕的OOP。
回答
我可以理解所有观点,但实际上,在这种情况下,C3.0的自动属性变得毫无用处。
你不能做这样的事情:
public class ConcreteClass : BaseClass { public override int Bar { get; private set; } }
IMO,C不应限制这种情况。开发人员有责任相应地使用它。
回答
我认为主要原因是语法太明确,以至于无法以其他任何方式工作。这段代码:
public override int MyProperty { get { ... } set { ... } }
很明显,get和set都是覆盖。基类中没有set,因此编译器会抱怨。就像我们无法覆盖基类中未定义的方法一样,我们也无法覆盖setter。
我们可能会说编译器应该猜测意图,并且仅将重写应用于可以被覆盖的方法(在这种情况下,即getter),但这违反了Cdesign原则之一,即编译器不得猜测意图,因为可能会在我们不知情的情况下猜测出错误。
我认为以下语法可能会做得很好,但是正如Eric Lippert一直说的那样,即使要实现这样的次要功能,仍然要付出很大的努力...
public int MyProperty { override get { ... } set { ... } }
或者,对于自动实现的属性,
public int MyProperty { override get; set; }
回答
这不是不可能的。我们只需要在媒体资源中使用" new"关键字即可。例如,
namespace { public class Base { private int _baseProperty = 0; public virtual int BaseProperty { get { return _baseProperty; } } } public class Test : Base { private int _testBaseProperty = 5; public new int BaseProperty { get { return _testBaseProperty; } set { _testBaseProperty = value; } } } }
看来这种方法满足了讨论的双方。使用" new"会破坏基类实现和子类实现之间的契约。当一个类可以具有多个合同(通过接口或者基类)时,这是必需的。
希望这可以帮助
回答
我今天偶然发现了同样的问题,我认为有一个很正当的理由想要这个。
首先,我想争论一下,拥有仅获取属性并不一定会转换为只读。我将其解释为"可以从此接口/抽象获取此值",这并不意味着该接口/抽象类的某些实现不需要用户/程序显式设置此值。抽象类用于实现部分所需功能的目的。我绝对没有理由说,继承的类不能在不违反任何合同的情况下添加setter。
以下是我今天需要的简化示例。我最终不得不在我的界面中添加一个setter来解决这个问题。添加设置器而不添加例如SetProp方法的原因是,接口的一个特定实现使用DataContract / DataMember进行Prop的序列化,如果我仅出于此目的而添加另一个属性,这将变得不必要地复杂。序列化。
interface ITest { // Other stuff string Prop { get; } } // Implements other stuff abstract class ATest : ITest { abstract public string Prop { get; } } // This implementation of ITest needs the user to set the value of Prop class BTest : ATest { string foo = "BTest"; public override string Prop { get { return foo; } set { foo = value; } // Not allowed. 'BTest.Prop.set': cannot override because 'ATest.Prop' does not have an overridable set accessor } } // This implementation of ITest generates the value for Prop itself class CTest : ATest { string foo = "CTest"; public override string Prop { get { return foo; } // set; // Not needed } }
我知道这只是"我的2美分"的帖子,但是我对原始的海报感到沮丧,并试图合理地认为这对我来说是一件好事,特别是考虑到直接从继承人继承时没有相同的限制界面。
同样,关于使用new而不是override的提述在这里也不适用,它根本不起作用,即使这样做也不会给我们想要的结果,即接口所描述的虚拟getter。