覆盖读/写属性以使其成为只读

时间:2022-01-07 21:07:26

I'm writing a custom control in C# based on TextBox. I want the Text property of my control to be read-only (since only certain values are allowed in the textbox and I don't want the calling program to be able to put invalid values in there.)

我正在基于TextBox在C#中编写自定义控件。我希望我的控件的Text属性是只读的(因为在文本框中只允许某些值,我不希望调用程序能够在其中放置无效值。)

I don't see how to do this. If I use public override Text {} the compiler inserts default get and set accessors. If I comment out the set accessor, the code compiles and runs, which I assume means the base accessor is being used. If I put readonly in the definition of my property, the compiler throws an error.

我不知道该怎么做。如果我使用公共覆盖Text {},编译器会插入默认的get和set访问器。如果我注释掉set访问器,则代码编译并运行,我假设这意味着正在使用基本访问器。如果我将readonly放在我的属性的定义中,编译器会抛出一个错误。

Any help would be greatly appreciated.

任何帮助将不胜感激。

6 个解决方案

#1


The issue here is that by trying to make a read/write property read-only, you're violating the contract of your base class. Your base class explicitly states that this property can be both retrieved and modified. Derived classes cannot break the contracts of their base classes, otherwise polymorphism would fail. Remember that if B derives from A, anywhere an A object is accepted, B must perform.

这里的问题是,通过尝试将读/写属性设置为只读,您违反了基类的合同。您的基类明确声明可以检索和修改此属性。派生类不能破坏其基类的契约,否则多态会失败。请记住,如果B派生自A,则在接受A对象的任何地方,B必须执行。

If that should not be the case for the derived class, then I would first question the design -- should this truly be derived from the base class? Is this truly an "is-a" relationship?

如果派生类不应该这样,那么我首先会质疑设计 - 这应该真的来自基类吗?这真的是一种“是一种”关系吗?

Assuming that we passed that test and truly should be derived, one approach -- which I don't personally like, but will share anyway -- may be to simply have the setter throw an exception. I personally don't like this, it still violates the contract, and feels like an excessive amount of cognitive friction in using your derived class, but if you truly have that solid a reason to do so ... well ... OK, I guess. But be sure you know WHY you're hacking around this.

假设我们通过了那个测试而且确实应该派生出来,一种方法 - 我个人不喜欢,但无论如何也会分享 - 可能只是让setter抛出异常。我个人不喜欢这样,它仍然违反了合同,并且在使用派生类时感觉过度认知摩擦,但如果你真的有这样的理由那么这样做......好吧......好吧,我猜。但是你要确定你知道为什么你正在乱砍这个。

But go back to the first point: Is this truly a scenario where a derived class is the right answer, from a design standpoint? Without seeing your code, my gut reaction is a no.

但回到第一点:从设计的角度来看,这真的是派生类是正确答案的场景吗?没有看到你的代码,我的直觉反应是否定的。

EDIT

Somehow in my initial read of this, I missed the fact that we're talking about a UI control here. I stand by what I wrote above this edit, in the general property overriding case. However, in the case of a UI control, where the polymorphism opportunities (I hope) will be somewhat limited, and where the need may well justify this design, then my recommendation would be to have the Text property setter throw an exception.

不知何故,在我最初的阅读中,我错过了我们在这里讨论UI控件的事实。我支持我在上面编辑的内容,在一般属性覆盖案例中。但是,在UI控件的情况下,多态性机会(我希望)会有所限制,并且需要可能证明这个设计是合理的,那么我的建议是让Text属性设置器抛出异常。

A better design, in my opinion, would be composition. Make a user control, add the TextBox to the user control design surface with Dock=Fill, and then expose a new Text property which is readonly. You can also now expose only those properties you really want to.

在我看来,更好的设计是构图。创建用户控件,使用Dock = Fill将TextBox添加到用户控件设计图面,然后公开一个只读的新Text属性。您现在也可以只公开您真正想要的属性。

#2


You have to hide the base property by declaring Text with the "new" keyword.

您必须通过使用“new”关键字声明Text来隐藏基本属性。

public new string Text { get; private set;}

You can't change access rights with an override, so you cannot use the override keyword, as you've seen. This way will "hide" the base implementation of Text, which isn't always a good idea, but as long as you know the risks it will work for you.

您无法使用覆盖更改访问权限,因此您无法使用override关键字,如您所见。这种方式将“隐藏”Text的基本实现,这并不总是一个好主意,但只要您知道它将对您有用的风险。

#3


public new string Text { get; private set;}

#4


Aside from others comments on overall design issues (which John Ruby explains nicely), you could always just override the setter to do nothing or throw an exception.

除了其他人对整体设计问题的评论(John Ruby很好地解释),你总是可以覆盖setter来做什么或抛出异常。

#5


I don't think that shadowing the property is the right answer, because any reference through the base will still work. Since the property is virtual, you can override it and implement a setter that ignores changes or throws an exception. I know it seems less clean, but it has the advantage of actually working.

我不认为影响财产是正确的答案,因为通过基地的任何参考仍然有效。由于属性是虚拟的,因此您可以覆盖它并实现忽略更改或引发异常的setter。我知道它似乎不太干净,但它具有实际工作的优势。

edit

Looks like Yuriy Faktorovich made the same suggestion before I could.

看起来Yuriy Faktorovich在我能做之前提出了同样的建议。

#6


I think John Rudy may have a point here. Inheritance is not the answer to everything. If what you need to do is restrict a textbox so that only valid values can be accepted then perhaps you want to use a validation control to make sure that only certain values are allowed.

我认为约翰鲁迪可能在这里有一点意见。继承不是一切的答案。如果您需要做的是限制文本框以便只接受有效值,那么您可能希望使用验证控件来确保只允许某些值。

This way you get the behaviour you want by making use of the components Microsoft have provided us with. No inherited class necessary.

这样,您可以通过使用Microsoft提供给我们的组件来获得您想要的行为。不需要继承类。

Edit: If you create a subclass every time you want to modify the behaviour of a UI control you will quickly have an unruly inheritance hierarchy on your hands. For this reason I would suggest you consider composition to modify UI controls.

编辑:如果每次要修改UI控件的行为时都创建子类,则很快就会有一个不守规矩的继承层次结构。出于这个原因,我建议你考虑组合来修改UI控件。

#1


The issue here is that by trying to make a read/write property read-only, you're violating the contract of your base class. Your base class explicitly states that this property can be both retrieved and modified. Derived classes cannot break the contracts of their base classes, otherwise polymorphism would fail. Remember that if B derives from A, anywhere an A object is accepted, B must perform.

这里的问题是,通过尝试将读/写属性设置为只读,您违反了基类的合同。您的基类明确声明可以检索和修改此属性。派生类不能破坏其基类的契约,否则多态会失败。请记住,如果B派生自A,则在接受A对象的任何地方,B必须执行。

If that should not be the case for the derived class, then I would first question the design -- should this truly be derived from the base class? Is this truly an "is-a" relationship?

如果派生类不应该这样,那么我首先会质疑设计 - 这应该真的来自基类吗?这真的是一种“是一种”关系吗?

Assuming that we passed that test and truly should be derived, one approach -- which I don't personally like, but will share anyway -- may be to simply have the setter throw an exception. I personally don't like this, it still violates the contract, and feels like an excessive amount of cognitive friction in using your derived class, but if you truly have that solid a reason to do so ... well ... OK, I guess. But be sure you know WHY you're hacking around this.

假设我们通过了那个测试而且确实应该派生出来,一种方法 - 我个人不喜欢,但无论如何也会分享 - 可能只是让setter抛出异常。我个人不喜欢这样,它仍然违反了合同,并且在使用派生类时感觉过度认知摩擦,但如果你真的有这样的理由那么这样做......好吧......好吧,我猜。但是你要确定你知道为什么你正在乱砍这个。

But go back to the first point: Is this truly a scenario where a derived class is the right answer, from a design standpoint? Without seeing your code, my gut reaction is a no.

但回到第一点:从设计的角度来看,这真的是派生类是正确答案的场景吗?没有看到你的代码,我的直觉反应是否定的。

EDIT

Somehow in my initial read of this, I missed the fact that we're talking about a UI control here. I stand by what I wrote above this edit, in the general property overriding case. However, in the case of a UI control, where the polymorphism opportunities (I hope) will be somewhat limited, and where the need may well justify this design, then my recommendation would be to have the Text property setter throw an exception.

不知何故,在我最初的阅读中,我错过了我们在这里讨论UI控件的事实。我支持我在上面编辑的内容,在一般属性覆盖案例中。但是,在UI控件的情况下,多态性机会(我希望)会有所限制,并且需要可能证明这个设计是合理的,那么我的建议是让Text属性设置器抛出异常。

A better design, in my opinion, would be composition. Make a user control, add the TextBox to the user control design surface with Dock=Fill, and then expose a new Text property which is readonly. You can also now expose only those properties you really want to.

在我看来,更好的设计是构图。创建用户控件,使用Dock = Fill将TextBox添加到用户控件设计图面,然后公开一个只读的新Text属性。您现在也可以只公开您真正想要的属性。

#2


You have to hide the base property by declaring Text with the "new" keyword.

您必须通过使用“new”关键字声明Text来隐藏基本属性。

public new string Text { get; private set;}

You can't change access rights with an override, so you cannot use the override keyword, as you've seen. This way will "hide" the base implementation of Text, which isn't always a good idea, but as long as you know the risks it will work for you.

您无法使用覆盖更改访问权限,因此您无法使用override关键字,如您所见。这种方式将“隐藏”Text的基本实现,这并不总是一个好主意,但只要您知道它将对您有用的风险。

#3


public new string Text { get; private set;}

#4


Aside from others comments on overall design issues (which John Ruby explains nicely), you could always just override the setter to do nothing or throw an exception.

除了其他人对整体设计问题的评论(John Ruby很好地解释),你总是可以覆盖setter来做什么或抛出异常。

#5


I don't think that shadowing the property is the right answer, because any reference through the base will still work. Since the property is virtual, you can override it and implement a setter that ignores changes or throws an exception. I know it seems less clean, but it has the advantage of actually working.

我不认为影响财产是正确的答案,因为通过基地的任何参考仍然有效。由于属性是虚拟的,因此您可以覆盖它并实现忽略更改或引发异常的setter。我知道它似乎不太干净,但它具有实际工作的优势。

edit

Looks like Yuriy Faktorovich made the same suggestion before I could.

看起来Yuriy Faktorovich在我能做之前提出了同样的建议。

#6


I think John Rudy may have a point here. Inheritance is not the answer to everything. If what you need to do is restrict a textbox so that only valid values can be accepted then perhaps you want to use a validation control to make sure that only certain values are allowed.

我认为约翰鲁迪可能在这里有一点意见。继承不是一切的答案。如果您需要做的是限制文本框以便只接受有效值,那么您可能希望使用验证控件来确保只允许某些值。

This way you get the behaviour you want by making use of the components Microsoft have provided us with. No inherited class necessary.

这样,您可以通过使用Microsoft提供给我们的组件来获得您想要的行为。不需要继承类。

Edit: If you create a subclass every time you want to modify the behaviour of a UI control you will quickly have an unruly inheritance hierarchy on your hands. For this reason I would suggest you consider composition to modify UI controls.

编辑:如果每次要修改UI控件的行为时都创建子类,则很快就会有一个不守规矩的继承层次结构。出于这个原因,我建议你考虑组合来修改UI控件。