【问题标题】:Overriding a read/write property to make it readonly覆盖读/写属性以使其只读
【发布时间】:2009-07-14 18:29:48
【问题描述】:

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

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

任何帮助将不胜感激。

【问题讨论】:

    标签: c#


    【解决方案1】:

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

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

    假设我们通过了该测试并且确实应该派生,一种方法(我个人不喜欢,但无论如何都会分享)可能是简单地让 setter 抛出异常。我个人不喜欢这样,它仍然违反合同,并且感觉在使用派生类时会产生过多的认知摩擦,但如果你真的有足够的理由这样做......好吧......好吧,我猜。但请确保您知道为什么要破解这个问题。

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

    编辑

    不知何故,在我最初阅读这篇文章时,我错过了我们在这里谈论 UI 控件的事实。在一般属性覆盖的情况下,我支持我在此编辑上方所写的内容。但是,在 UI 控件的情况下,多态性机会(我希望)会受到一定的限制,并且需要很可能证明这种设计是合理的,那么我的建议是让 Text 属性设置器抛出异常。

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

    【讨论】:

    • +1 作曲,这似乎是这里的合乎逻辑的选择(尽管我没有想到 :))
    【解决方案2】:

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

    public new string Text { get; private set;}
    

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

    【讨论】:

    • 这将创建一个新属性,而不是覆盖现有属性。这样做会增加混乱,因为人们希望能够在基础中设置属性并使其流向派生对象。
    • 同意新的财产。部分不同意混淆。能够做到这一点的全部目的是能够控制您的 API 的感知复杂性——如果这是一个向最终用户隐藏潜在复杂或危险功能的商业决策,那么这是一个不错的决策。 “新”的每一个用法都需要仔细考虑。
    • 当某人有一个基础对象,设置属性,然后转换你的对象时会发生什么? - 财产价值将丢失。隐藏的方法和属性仍然可以通过转换为基类来使用。此外,当您尝试将新的派生对象传递给接受基类作为参数的方法时会发生什么?您的财产价值也会在那里丢失 - 这是任何人都不会期望发生的事情。
    • 我完全同意你的看法。我只是回答直接的问题。 “新”关键字的正确使用需要从经验中学习。但是,并非您所做的每个控件都旨在扩展甚至暴露给其他程序员,并且有很多正当理由。使用这种语言是有原因的 ;)
    【解决方案3】:
    public new string Text { get; private set;}
    

    【讨论】:

    • “new”关键字带来了隐藏与压倒一切的问题:bit.ly/Ctfmb
    • 在 Web 控件中它是一个虚拟属性。
    • Yuriy:是的,它是一个虚拟财产。但是,属性签名的一部分是 getter/setter 的可见性级别。尝试像这样编译代码会导致编译器错误:“在覆盖 'public' 继承的成员 'classname.Text.set' 时无法更改访问修饰符”
    • 这将创建一个新的属性,而不是覆盖现有的。这样做会增加混乱,因为人们希望能够在基础中设置属性并使其流向派生对象。
    • 既然无法停止投射,不如将其设为公共集,但覆盖它以在集中抛出错误?
    【解决方案4】:

    除了其他关于整体设计问题的 cmets(John Ruby 很好地解释了这一点)之外,您总是可以重写 setter 以不做任何事情或抛出异常。

    【讨论】:

      【解决方案5】:

      我不认为隐藏属性是正确的答案,因为通过基础的任何引用仍然有效。由于该属性是虚拟的,因此您可以覆盖它并实现一个忽略更改或引发异常的设置器。我知道它看起来不太干净,但它具有实际工作的优势。

      编辑

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

      【讨论】:

        【解决方案6】:

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

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-12-02
          • 1970-01-01
          • 1970-01-01
          • 2011-07-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-07
          相关资源
          最近更新 更多