C# 为什么不可能覆盖 getter-only 属性并添加 setter?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/82437/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why is it impossible to override a getter-only property and add a setter?
提问by ripper234
Why is the following C# code not allowed:
为什么不允许使用以下 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': cannot override because 'BaseClass.Bar' does not have an overridable set accessor
CS0546“ConcreteClass.Bar.set”:无法覆盖,因为“BaseClass.Bar”没有可覆盖的集合访问器
采纳答案by Gishu
Because the writer of Baseclass has explicitly declared that Bar has to be a read-only property. It doesn't make sense for derivations to break this contract and make it read-write.
因为 Baseclass 的编写者已经明确声明 Bar 必须是只读属性。推导破坏这个契约并使其可读写是没有意义的。
I'm with Microsoft on this one.
Let's say I'm a new programmer who has been told to code against the Baseclass derivation. i write something that assumes that Bar cannot be written to (since the Baseclass explicitly states that it is a get only property).
Now with your derivation, my code may break. e.g.
我在这方面与微软合作。
假设我是一名新程序员,他被告知要针对 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; }
}
Since Bar cannot be set as per the BaseClass interface, BarProvider assumes that caching is a safe thing to do - Since Bar cannot be modified. But if set was possible in a derivation, this class could be serving stale values if someone modified the _source object's Bar property externally. The point being 'Be Open, avoid doing sneaky things and surprising people'
因为 Bar 不能按照 BaseClass 接口设置,所以 BarProvider 假设缓存是一件安全的事情 - 因为 Bar 不能修改。但是,如果在派生中可以设置 set,那么如果有人在外部修改了 _source 对象的 Bar 属性,则此类可能会提供陈旧的值。重点是“保持开放,避免做偷偷摸摸的事情和让人们感到惊讶”
Update: Ilya Ryzhenkov asks 'Why don't interfaces play by the same rules then?'Hmm.. this gets muddier as I think about it.
An interface is a contract that says 'expect an implementation to have a read property named Bar.' PersonallyI'm much less likely to make that assumption of read-only if I saw an Interface. When i see a get-only property on an interface, I read it as 'Any implementation would expose this attribute Bar'... on a base-class it clicks as 'Bar is a read-only property'. Of course technically you're not breaking the contract.. you're doing more. So you're right in a sense.. I'd close by saying 'make it as hard as possible for misunderstandings to crop up'.
更新:Ilya Ryzhenkov 问道“那为什么接口不按照相同的规则运行呢?” 嗯..这让我越想越糊涂。
接口是一个约定,它说“期望实现具有名为 Bar 的读取属性”。就我个人而言,如果我看到一个接口,我就不太可能做出只读的假设。当我在接口上看到一个 get-only 属性时,我把它读为“任何实现都会公开这个属性 Bar”...在基类上它点击为“Bar 是一个只读属性”。当然,从技术上讲,您并没有违反合同……您做得更多。所以从某种意义上说,你是对的......我最后会说'尽可能地避免误解的出现'。
回答by Ishmaeel
Because at the IL level, a read/write property translates into two (getter and setter) methods.
因为在 IL 级别,读/写属性转换为两个(getter 和 setter)方法。
When overriding, you have to keep supporting the underlying interface. If you could add a setter, you would effectively be adding a new method, which would remain invisible to the outside world, as far as your classes' interface was concerned.
覆盖时,您必须继续支持底层接口。如果您可以添加一个 setter,那么您实际上就是在添加一个新方法,就您的类的接口而言,该方法对外界仍然不可见。
True, adding a new method would not be breaking compatibility per se, but since it would remain hidden, decision to disallow this makes perfect sense.
确实,添加新方法本身不会破坏兼容性,但由于它会保持隐藏状态,因此决定禁止这样做是完全合理的。
回答by petr k.
Because that would break the concept of encapsulation and implementation hiding. Consider the case when you create a class, ship it, and then the consumer of your class makes himself able to set a property for which you originally provide a getter only. It would effectively disrupt any invariants of your class which you can depend on in your implementation.
因为那会打破封装和实现隐藏的概念。考虑这样一种情况,当您创建一个类并发布它,然后您的类的使用者让他自己能够设置一个您最初只为其提供一个 getter 的属性。它会有效地破坏您在实现中可以依赖的类的任何不变量。
回答by Hallgrim
You could perhaps go around the problem by creating a new property:
你也许可以通过创建一个新属性来解决这个问题:
public new int Bar
{
get { return 0; }
set {}
}
int IBase.Bar {
get { return Bar; }
}
回答by Joel Coehoorn
Because a class that has a read-only property (no setter) probably has a good reason for it. There might not be any underlying datastore, for example. Allowing you to create a setter breaks the contract set forth by the class. It's just bad OOP.
因为具有只读属性(无 setter)的类可能有充分的理由。例如,可能没有任何底层数据存储。允许您创建 setter 违反了类规定的契约。这只是糟糕的 OOP。
回答by Thomas Danecker
I can understand all your points, but effectively, C# 3.0's automatic properties get useless in that case.
我可以理解您的所有观点,但实际上,在这种情况下,C# 3.0 的自动属性变得毫无用处。
You can't do anything like that:
你不能做这样的事情:
public class ConcreteClass : BaseClass
{
public override int Bar
{
get;
private set;
}
}
IMO, C# should not restrict such scenarios. It's the responsibility of the developer to use it accordingly.
IMO、C# 不应限制此类场景。开发人员有责任相应地使用它。
回答by Roman Starkov
I think the main reason is simply that the syntax is too explicit for this to work any other way. This code:
我认为主要原因很简单,语法太明确了,无法以任何其他方式工作。这段代码:
public override int MyProperty { get { ... } set { ... } }
is quite explicit that both the get
and the set
are overrides. There is no set
in the base class, so the compiler complains. Just like you can't override a method that's not defined in the base class, you can't override a setter either.
非常明确,theget
和 theset
都是覆盖。set
基类中没有,所以编译器会抱怨。就像您不能覆盖未在基类中定义的方法一样,您也不能覆盖 setter。
You might say that the compiler should guess your intention and only apply the override to the method that can be overridden (i.e. the getter in this case), but this goes against one of the C# design principles - that the compiler must not guess your intentions, because it may guess wrong without you knowing.
您可能会说编译器应该猜测您的意图,并且只将覆盖应用于可以被覆盖的方法(即本例中的 getter),但这违背了 C# 设计原则之一——编译器不能猜测您的意图,因为它可能会在你不知情的情况下猜错。
I think the following syntax might do nicely, but as Eric Lippert keeps saying, implementing even a minor feature like this is still a major amount of effort...
我认为以下语法可能会很好,但正如 Eric Lippert 一直说的那样,即使实现这样的小功能仍然需要大量的努力......
public int MyProperty
{
override get { ... }
set { ... }
}
or, for autoimplemented properties,
或者,对于自动实现的属性,
public int MyProperty { override get; set; }
回答by mbolt35
This is not impossible. You simply have to use the "new" keyword in your property. For example,
这并非不可能。您只需在您的财产中使用“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;
}
}
}
}
It appears as if this approach satisfies both sides of this discussion. Using "new" breaks the contract between the base class implementation and the subclass implementation. This is necessary when a Class can have multiple contracts (either via interface or base class).
似乎这种方法满足了讨论的双方。使用“new”打破了基类实现和子类实现之间的契约。当一个类可以有多个契约(通过接口或基类)时,这是必要的。
Hope this helps
希望这可以帮助
回答by JJJ
I stumbled across the very same problem today and I think a have a very valid reason for wanting this.
我今天偶然发现了同样的问题,我认为有一个非常正当的理由想要这个。
First I'd like to argue that having a get-only property doesn't necessarily translate into read-only. I interpret it as "From this interface/abtract you can get this value", that doesn't mean that some implementation of that interface/abstract class wont need the user/program to set this value explicitly. Abstract classes serve the purpose of implementing part of the needed functionality. I see absolutely no reason why an inherited class couldn't add a setter without violating any contracts.
首先,我想争辩说,拥有一个 get-only 属性并不一定会转化为只读。我将它解释为“从这个接口/抽象你可以得到这个值”,这并不意味着该接口/抽象类的某些实现不需要用户/程序显式设置这个值。抽象类用于实现部分所需功能。我完全看不出为什么继承的类不能在不违反任何合同的情况下添加 setter。
The following is a simplified example of what I needed today. I ended up having to add a setter in my interface just to get around this. The reason for adding the setter and not adding, say, a SetProp method is that one particular implementation of the interface used DataContract/DataMember for serialization of Prop, which would have been made needlessly complicated if I had to add another property just for the purpose of serialization.
以下是我今天需要的简化示例。我最终不得不在我的界面中添加一个 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
}
}
I know this is just a "my 2 cents" post, but I feel with the original poster and trying to rationalize that this is a good thing seems odd to me, especially considering that the same limitations doesn't apply when inheriting directly from an interface.
我知道这只是一个“我的 2 美分”帖子,但我觉得原始海报并试图合理化这是一件好事对我来说似乎很奇怪,特别是考虑到直接从界面。
Also the mention about using new instead of override does not apply here, it simply doesn't work and even if it did it wouldn't give you the result wanted, namely a virtual getter as described by the interface.
此外,关于使用 new 而不是 override 的提及在此处不适用,它根本不起作用,即使这样做也不会给您想要的结果,即接口描述的虚拟 getter。
回答by supercat
The problem is that for whatever reason Microsoft decided that there should be three distinct types of properties: read-only, write-only, and read-write, only one of which may exist with a given signature in a given context; properties may only be overridden by identically-declared properties. To do what you want it would be necessary to create two properties with the same name and signature--one of which was read-only, and one of which was read-write.
问题是,无论出于何种原因,Microsoft 决定应该存在三种不同类型的属性:只读、只写和读写,在给定的上下文中,给定的签名可能只存在其中一种;属性只能被相同声明的属性覆盖。要执行您想要的操作,必须创建两个具有相同名称和签名的属性——其中一个是只读的,另一个是读写的。
Personally, I wish that the whole concept of "properties" could be abolished, except that property-ish syntax could be used as syntactic sugar to call "get" and "set" methods. This would not only facilitate the 'add set' option, but would also allow for 'get' to return a different type from 'set'. While such an ability wouldn't be used terribly often, it could sometimes be useful to have a 'get' method return a wrapper object while the 'set' could accept either a wrapper or actual data.
就我个人而言,我希望“属性”的整个概念可以被废除,除了可以使用类似属性的语法作为语法糖来调用“get”和“set”方法。这不仅有利于 'add set' 选项,而且还允许 'get' 返回与 'set' 不同的类型。虽然这种能力不会经常使用,但有时让“get”方法返回包装器对象而“set”可以接受包装器或实际数据有时会很有用。