为什么不可能覆盖仅 getter 的属性并添加 setter?
为什么不允许使用以下 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”没有可重写的设置访问器
Why is the following C# code not allowed:
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(16)
我认为主要原因很简单,语法太明确,无法以任何其他方式工作。这段代码
非常明确,
get
和set
都被覆盖。基类中没有set
,因此编译器会抱怨。就像您无法重写基类中未定义的方法一样,您也无法重写 setter。您可能会说编译器应该猜测您的意图,并且仅将重写应用于可以重写的方法(即本例中的 getter),但这违背了 C# 设计原则之一 - 编译器不得猜测您的意图,因为它可能在你不知情的情况下猜错了。
我认为下面的语法可能会做得很好,但正如 Eric Lippert 一直说的那样,即使实现这样的一个小功能仍然需要大量的努力......
或者,对于自动实现的属性,
I think the main reason is simply that the syntax is too explicit for this to work any other way. This code:
is quite explicit that both the
get
and theset
are overrides. There is noset
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.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.
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...
or, for autoimplemented properties,
这是有可能的。
tl;dr– 如果需要,您可以使用 setter 覆盖仅获取方法。它基本上只是:
创建一个
new
属性,该属性具有使用相同名称的get
和set
。覆盖
之前的get
,为新的get
起别名。这使我们能够使用
get
/set
覆盖属性,即使它们的基本定义中缺少 setter。情况:预先存在的仅
get
属性。您有一些无法修改的类结构。也许它只是一个类,或者它是一个预先存在的继承树。无论哪种情况,您都想向属性添加
set
方法,但无法做到。问题:无法仅使用
get
/set
来覆盖
get
。您想要使用
get
/set
属性来覆盖
,但它无法编译。解决方案:使用
抽象
中间层。虽然您无法直接使用
get
/set
属性覆盖
,但您可以:创建一个
new
get
/set
具有相同名称的属性。使用新
get
方法的访问器覆盖旧的get
方法以确保一致性。因此,首先编写抽象中间层:
然后编写之前无法编译的类。这次它会编译,因为您实际上并没有
覆盖
'仅get
属性;相反,您将使用new
关键字替换它。结果:一切正常!
讨论。
此方法允许您将
set
方法添加到get
-only 属性。您还可以使用它执行以下操作:将任何属性更改为仅
get
、set
-only 或get
- and-set
属性,无论它在基类中是什么。更改派生类中方法的返回类型。
主要缺点是需要编写更多代码,并且继承树中需要额外的
抽象类
。对于采用参数的构造函数来说,这可能有点烦人,因为必须将这些参数复制/粘贴到中间层中。奖励:您可以更改属性的返回类型。
作为奖励,您还可以根据需要更改返回类型。
如果基本定义仅是
get
,那么您可以使用更多派生的返回类型。如果基本定义仅是
set
,那么您可以使用较少派生的返回类型。如果基本定义已经是
get
/set
,则:您可以使用更派生的返回类型如果您将其设为
仅设置
;您可以使用派生较少的返回类型如果您将其设为
仅 get
。在所有情况下,如果需要,您可以保持相同的返回类型。
It's possible.
tl;dr– You can override a get-only method with a setter if you want. It's basically just:
Create a
new
property that has both aget
and aset
using the same name.override
the priorget
to alias the newget
.This enables us to override properties with
get
/set
even if they lacked a setter in their base definition.Situation: Pre-existing
get
-only property.You have some class structure that you can't modify. Maybe it's just one class, or it's a pre-existing inheritance tree. Whatever the case, you want to add a
set
method to a property, but can't.Problem: Can't
override
theget
-only withget
/set
.You want to
override
with aget
/set
property, but it won't compile.Solution: Use an
abstract
intermediate layer.While you can't directly
override
with aget
/set
property, you can:Create a
new
get
/set
property with the same name.override
the oldget
method with an accessor to the newget
method to ensure consistency.So, first you write the
abstract
intermediate layer:Then, you write the class that wouldn't compile earlier. It'll compile this time because you're not actually
override
'ing theget
-only property; instead, you're replacing it using thenew
keyword.Result: Everything works!
Discussion.
This method allows you to add
set
methods toget
-only properties. You can also use it to do stuff like:Change any property into a
get
-only,set
-only, orget
-and-set
property, regardless of what it was in a base class.Change the return type of a method in derived classes.
The main drawbacks are that there's more coding to do and an extra
abstract class
in the inheritance tree. This can be a bit annoying with constructors that take parameters because those have to be copy/pasted in the intermediate layer.Bonus: You can change the property's return-type.
As a bonus, you can also change the return type if you want.
If the base definition was
get
-only, then you can use a more-derived return type.If the base definition was
set
-only, then you can use a less-derived return type.If the base definition was already
get
/set
, then: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.In all cases, you can keep the same return type if you want.
我今天偶然发现了同样的问题,我认为我有一个非常充分的理由想要这个。
首先我想说的是,拥有只读属性并不一定意味着只读。我将其解释为“从这个接口/抽象类中您可以获得这个值”,这并不意味着该接口/抽象类的某些实现不需要用户/程序显式设置这个值。抽象类的目的是实现部分所需的功能。我认为继承类绝对没有理由不能在不违反任何契约的情况下添加 setter。
以下是我今天需要的简化示例。我最终不得不在界面中添加一个设置器来解决这个问题。添加 setter 而不是添加 SetProp 方法之类的原因是,接口的一个特定实现使用 DataContract/DataMember 来序列化 Prop,如果我必须为此目的添加另一个属性,那么这将变得不必要的复杂化的序列化。
我知道这只是一个“我的 2 美分”帖子,但我对原始海报的感觉并试图合理化这是一件好事对我来说似乎很奇怪,特别是考虑到直接从界面。
另外,关于使用 new 而不是 override 的提及在这里并不适用,它根本不起作用,即使它起作用,也不会给你想要的结果,即接口所描述的虚拟 getter。
I stumbled across the very same problem today and I think I 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/abstract class you can get this value", that doesn't mean that some implementation of that interface/abstract class won't 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.
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.
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.
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.
我同意不能重写派生类型中的 getter 是一种反模式。只读指定缺乏实现,而不是纯函数的合同(由最高投票答案暗示)。
我怀疑微软有这个限制,要么是因为宣扬了同样的误解,要么是因为简化了语法;不过,现在范围可以应用于单独的获取或设置,也许我们希望覆盖也可以。
最高投票答案所表明的误解是,只读属性应该在某种程度上比读/写属性更“纯粹”,这是荒谬的。简单看一下框架中很多常见的只读属性;该值不是常数/纯函数;例如,DateTime.Now 是只读的,但绝不是纯函数值。尝试“缓存”只读属性的值并假设下次会返回相同的值是有风险的。
无论如何,我使用了以下策略之一来克服这一限制;两者都不是完美的,但可以让你克服这种语言缺陷:
I agree that not being able to override a getter in a derived type is an anti-pattern. Read-Only specifies lack of implementation, not a contract of a pure functional (implied by the top vote answer).
I suspect Microsoft had this limitation either because the same misconception was promoted, or perhaps because of simplifying grammar; though, now that scope can be applied to get or set individually, perhaps we can hope override can be too.
The misconception indicated by the top vote answer, that a read-only property should somehow be more "pure" than a read/write property is ridiculous. Simply look at many common read only properties in the framework; the value is not a constant / purely functional; for example, DateTime.Now is read-only, but anything but a pure functional value. An attempt to 'cache' a value of a read only property assuming it will return the same value next time is risky.
In any case, I've used one of the following strategies to overcome this limitation; both are less than perfect, but will allow you to limp beyond this language deficiency:
您也许可以通过创建一个新属性来解决这个问题:
You could perhaps go around the problem by creating a new property:
我可以理解您的所有观点,但实际上,C# 3.0 的自动属性在这种情况下变得毫无用处。
你不能做这样的事情:
IMO,C# 不应该限制这样的场景。开发人员有责任相应地使用它。
I can understand all your points, but effectively, C# 3.0's automatic properties get useless in that case.
You can't do anything like that:
IMO, C# should not restrict such scenarios. It's the responsibility of the developer to use it accordingly.
问题是,无论出于何种原因,微软决定应该存在三种不同类型的属性:只读、只写和读写,在给定上下文中,只有其中一种可以以给定签名存在;属性只能被相同声明的属性覆盖。要执行您想要的操作,需要创建两个具有相同名称和签名的属性 - 其中一个是只读的,另一个是读写的。
就我个人而言,我希望“属性”的整个概念可以被废除,除了属性语法可以用作调用“get”和“set”方法的语法糖。这不仅有利于“add set”选项,而且还允许“get”返回与“set”不同的类型。虽然这种能力不会经常使用,但有时让“get”方法返回包装器对象而“set”可以接受包装器或实际数据可能会很有用。
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.
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.
这里有一个解决方法,可以使用反射来实现此目的:
通过这种方式,我们可以在只读属性上设置对象值。但可能并不适用于所有场景!
Here is a work-around in order to achieve this using Reflection:
This way we can set object value on a property that is readonly. Might not work in all the scenarios though!
您应该将问题标题更改为以下细节:您的问题仅与覆盖抽象属性有关,或者您的问题通常与覆盖类的仅获取属性有关。
如果是前者(覆盖抽象属性)
该代码是无用的。单独的基类不应告诉您必须重写 Get-Only 属性(可能是接口)。基类提供可能需要来自实现类的特定输入的通用功能。因此,公共功能可能会调用抽象属性或方法。在给定的情况下,常见的功能方法应该要求您重写抽象方法,例如:
但是如果您无法控制它,并且基类的功能从其自己的公共属性读取(奇怪),那么只需这样做:
我想指出(奇怪的)评论:我想说,最佳实践是类不使用其自己的公共属性,而是使用其存在的私有/受保护字段。所以这是一个更好的模式:
如果后者(覆盖类的 get-only 属性)
每个非抽象属性都有一个 setter。否则它是没有用的,你不应该关心使用它。微软不必允许你做你想做的事。原因是:setter 以某种形式存在,并且您可以轻松地完成您想要的Veerryy。
基类或任何可以使用
{get;}
读取属性的类,都具有该属性的 SOME 类公开设置器。元数据将如下所示:但实现将具有复杂性范围的两端:
最不复杂:
最复杂:
我的观点是,无论哪种方式,您都暴露了设置器。在您的情况下,您可能想要覆盖
int Bar
因为您不希望基类处理它,无权查看它是如何处理它的,或者被指派去修改一些代码真的太快了,而且违背了你的意愿。在后者和前者中(结论)
长话短说:微软没有必要改变任何东西。您可以选择如何设置实现类,并且在没有构造函数的情况下,使用全部基类或不使用基类。
You should alter your question title to either detail that your question is solely in regards to overriding an abstract property, or that your question is in regards to generally overriding a class's get-only property.
If the former (overriding an abstract property)
That code is useless. A base class alone shouldn't tell you that you're forced to override a Get-Only property (Perhaps an Interface). A base class provides common functionality which may require specific input from an implementing class. Therefore, the common functionality may make calls to abstract properties or methods. In the given case, the common functionality methods should be asking for you to override an abstract method such as:
But if you have no control over that, and the functionality of the base class reads from its own public property (weird), then just do this:
I want to point out the (weird) comment: I would say a best-practice is for a class to not use its own public properties, but to use its private/protected fields when they exist. So this is a better pattern:
If the latter (overriding a class's get-only property)
Every non-abstract property has a setter. Otherwise it's useless and you shouldn't care to use it. Microsoft doesn't have to allow you to do what you want. Reason being: the setter exists in some form or another, and you can accomplish what you want Veerryy easily.
The base class, or any class where you can read a property with
{get;}
, has SOME sort of exposed setter for that property. The metadata will look like this:But the implementation will have two ends of the spectrum of complexity:
Least Complex:
Most Complex:
My point being, either way, you have the setter exposed. In your case, you may want to override
int Bar
because you don't want the base class to handle it, don't have access to review how it's handling it, or were tasked to hax some code real quick'n'dirty against your will.In both Latter and Former (Conclusion)
Long-Story Short: It isn't necessary for Microsoft to change anything. You can choose how your implementing class is set up and, sans the constructor, use all or none of the base class.
仅适用于一小部分用例的解决方案,但尽管如此:在 C# 6.0 中,会自动添加“只读”setter 以覆盖仅 getter 属性。
Solution for only a small subset of use cases, but nevertheless: in C# 6.0 "readonly" setter is automatically added for overridden getter-only properties.
这并非不可能。您只需在您的属性中使用“new”关键字即可。例如,
这种方法似乎满足了讨论的双方。使用“new”打破了基类实现和子类实现之间的契约。当一个类可以有多个契约(通过接口或基类)时,这是必要的。
希望这有帮助
This is not impossible. You simply have to use the "new" keyword in your property. For example,
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).
Hope this helps
因为这会打破封装和实现隐藏的概念。考虑这样的情况:创建一个类,发布它,然后类的使用者使自己能够设置一个您最初仅为其提供 getter 的属性。它会有效地破坏您在实现中可以依赖的类的任何不变量。
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.
因为具有只读属性(无 setter)的类可能有充分的理由。例如,可能没有任何底层数据存储。允许您创建 setter 会破坏类规定的契约。这只是糟糕的 OOP。
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.
基类中的只读属性指示该属性表示始终可以从类内部确定的值(例如与对象的 (db-) 上下文匹配的枚举值)。因此,确定值的责任仍在班级内部。
添加 setter 会在这里导致一个尴尬的问题:
如果您将该值设置为除已有的单个可能值以外的任何值,则会发生验证错误。
不过,规则常常有例外。例如,在一个派生类中,上下文很有可能将可能的枚举值缩小到 10 个中的 3 个,但该对象的用户仍然需要决定哪一个是正确的。派生类需要将确定值的责任委托给该对象的用户。
重要的是要认识到,该对象的用户应该充分了解此异常并承担设置正确值的责任。
在这种情况下,我的解决方案是将属性保留为只读,并向派生类添加新的读写属性以支持异常。
覆盖原始属性将仅返回新属性的值。
新属性可以有一个正确的名称,正确地指示此异常的上下文。
这也印证了Gishu的一句话:“尽可能避免误解的出现”。
A read-only property in the base class indicates that this property represents a value that can always be determined from within the class (for example an enum value matching the (db-)context of an object). So the responsibillity of determining the value stays within the class.
Adding a setter would cause an awkward issue here:
A validation error should occur if you set the value to anything else than the single possible value it already has.
Rules often have exceptions, though. It is very well possible that for example in one derived class the context narrows the possible enum values down to 3 out of 10, yet the user of this object still needs to decide which one is correct. The derived class needs to delegate the responsibillity of determining the value to the user of this object.
Important to realize is that the user of this object should be well aware of this exception and assume the responsibillity to set the correct value.
My solution in these kind of situations would be to leave the property read-only and add a new read-write property to the derived class to support the exception.
The override of the original property will simply return the value of the new property.
The new property can have a proper name indicating the context of this exception properly.
This also supports the valid remark: "make it as hard as possible for misunderstandings to crop up" by Gishu.
因为在 IL 级别,读/写属性会转换为两个(getter 和 setter)方法。
重写时,您必须继续支持底层接口。如果您可以添加一个设置器,那么您将有效地添加一个新方法,就您的类的接口而言,该方法对外界来说是不可见的。
确实,添加新方法本身不会破坏兼容性,但由于它将保持隐藏状态,因此决定禁止这样做是完全有道理的。
Because at the IL level, a read/write property translates into two (getter and setter) methods.
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.
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.
因为 Baseclass 的作者已明确声明 Bar 必须是只读属性。推导打破这个契约并使其可读写是没有意义的。
我与微软合作解决这个问题。
假设我是一名新程序员,被告知要针对基类派生进行编码。我编写了一些假设 Bar 无法写入的内容(因为 Baseclass 明确声明它是仅获取属性)。
现在根据你的推导,我的代码可能会崩溃。例如,
由于 Bar 无法按照 BaseClass 接口进行设置,BarProvider 假定缓存是安全的事情 - 因为 Bar 无法修改。但是,如果在派生中可以设置,则如果有人在外部修改 _source 对象的 Bar 属性,则此类可能会提供过时的值。要点是“保持开放,避免做偷偷摸摸的事情和让别人惊讶的事情”
更新:Ilya Ryzhenkov 问道“那么为什么界面不遵循相同的规则呢?” '
嗯..随着我的思考,这变得更加混乱。
接口是一个约定,其中规定“期望实现具有名为 Bar 的读取属性”。 就我个人而言,如果我看到一个接口,我就不太可能做出只读的假设。当我在接口上看到仅获取属性时,我将其读为“任何实现都会公开此属性 Bar”...在基类上,它单击为“Bar 是只读属性”。当然,从技术上讲,你并没有违反合同……你做得更多。所以从某种意义上来说你是对的……我最后想说的是“尽可能地避免误解的出现”。
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.
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.
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'
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.' Personally I'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'.