【问题标题】:Are protected members/fields really that bad?受保护的成员/字段真的那么糟糕吗?
【发布时间】:2011-03-12 02:12:18
【问题描述】:

现在,如果您阅读 MSDN for C# 中的命名约定,您会注意到它声明属性始终优先于公共和受保护字段。有些人甚至告诉我,你永远不应该使用公共或受保护的字段。现在我同意我还没有找到需要公共字段但受保护字段真的那么糟糕的原因吗?

如果您需要确保在获取/设置值时执行某些验证检查,我可以看到它,但在我看来,很多时候这似乎只是额外的开销。我的意思是说我有一个 GameItem 类,其中包含 baseName、prefixName 和 suffixName 字段。为什么我要承担创建属性 (C#) 或访问器方法的开销以及我会发生的性能损失(如果我对应用程序中的每个字段都这样做,我相信它加起来会少尤其是在某些语言中,例如PHP 或某些性能至关重要的应用程序(例如游戏)?

【问题讨论】:

    标签: c# performance coding-style access-levels


    【解决方案1】:

    受保护的成员/字段真的那么糟糕吗?

    没有。它们是非常非常糟糕的。

    一旦某个成员比private 更易于访问,您就可以向其他类保证该成员的行为方式。由于一个字段是完全不受控制的,因此将其“放在野外”会打开您的类以及从您的类继承或与您的类交互的类,从而带来更高的错误风险。无法知道字段何时更改,也无法控制谁或什么更改了它。

    如果现在或将来某个时候,您的任何代码曾经依赖于某个特定值的字段,那么您现在必须添加有效性检查和回退逻辑,以防它不是预期值 - 您使用它的每个地方.当你可以把它变成一个该死的财产时,这是一个巨大的浪费;)

    与派生类共享信息的最佳方法是只读属性

    protected object MyProperty { get; }
    

    如果你绝对让它读/写,不要。如果你真的,真的必须让它读写,重新考虑你的设计。如果您仍然需要它进行读写,请向您的同事道歉并且不要再这样做了:)

    许多开发人员认为 - 并且会告诉您 - 这过于严格。确实,您可以通过而不必那么严格。但是采用这种方法将帮助您从勉强应付到非常强大的软件。您将花费更少的时间来修复错误。

    关于性能方面的任何担忧 - 不要。我保证在你的整个职业生涯中,你永远不会写出如此快的代码,以至于瓶颈是调用堆栈本身。

    【讨论】:

    • +1,如果可以的话。一旦您将成员设为非私有成员,您就会永远被它困住,直到永远。它现在是您的公共界面。
    • 我基本同意,但我认为拥有可写保护属性没有问题。毕竟它只是带有参数的方法的语法糖,我认为很多人不会争辩说受保护的方法不能有任何参数。 (目前在方法和属性之间进行选择时,我忽略了细微的语义差异。)
    • @Daniel 使属性可写增加了一个数量级的复杂性。在现实世界中,每个 类都是不可变的肯定是不现实的,但是如果你的大多数类都是不可变的,那么编写无错误的代码会非常容易。我曾经有过这样的启示,我希望能帮助其他人拥有它。
    • @Daniel(澄清一下,我区分表示状态的属性和表示动作的方法)
    • @ryanzec 你成功了!消费者应该只能在初始化时设置对象的状态(通过构造函数)。一旦对象被激活,它就应该在内部对自己的状态生命周期负责。允许消费者影响状态会增加不必要的复杂性和风险。
    【解决方案2】:

    好的,投反对票时间。

    • 首先,属性永远不会损害性能(只要它们不做太多)。其他人都是这么说的,我同意。

    • 还有一点是属性很好,因为您可以在其中放置断点来捕获获取/设置事件并找出它们的来源。

    其余的争论以这种方式困扰着我:

    • 它们听起来像是“声望的争论”。如果 MSDN 这么说,或者大家都喜欢的著名开发者或作者这么说,那一定是这样的。

    • 它们基于这样一种思想,即数据结构具有许多不一致的状态,并且必须防止漂移或被置于这些状态中。由于(在我看来)数据结构在当前教学中被过分强调,因此通常它们确实需要这些保护。更可取的是最小化数据结构,以便它趋于规范化并且不具有不一致的状态。然后,如果一个类的成员发生了变化,它只是被改变了,而不是被损坏了。毕竟,不知何故,很多优秀的软件都是用 C 语言编写的,而这并没有因为缺乏保护而受到严重影响。

    • 它们基于极端的防御性编码。它基于这样的想法,即您的类将用于一个无法信任其他人的代码不会使您的东西变得笨拙的世界。我确信在某些情况下确实如此,但从未见过它们。我已经看到的情况是,为了绕过不必要的保护,并试图保护过于复杂和非规范化的数据结构的一致性,事情变得非常复杂.

    【讨论】:

    • 非常好的点。我只想提出一个想法:如果您在一个超过 1 人的团队中工作,那么您的代码就在野外。合同未明确强制执行的任何内容都容易受到误解。通过消除歧义并通过设计强制执行信息流,它可以为您的队友提供出色的服务,并减少每个人的工作量。
    • Python 基本上没有 私有方法,更不用说受保护的方法了,而且在实践中也没有什么大不了的。
    • @Rex:我知道这是公认的智慧,我当然可以理解,但它仍然让我印象深刻,因为人们相信抽象,而不是从艰苦的经验中知道。我一直发现,如果你在广泛持有的信念背后/下寻找,你会发现有用的宝石。 (顺便说一句,我确实在一个规模很大的团队中工作。)
    • 也许大多数人只是抽象地相信它,但自从我开始实践它并为我的团队制定政策后,生活变得更加愉快。产量上升,错误率下降,幸福感上升。毫无疑问,改变以这种方式工作是一座难以攀登的山峰。 IMO 非常值得。
    【解决方案3】:

    关于字段与属性,我可以想到两个首选公共接口中的属性的原因(受保护的也是公共的,因为除了你的类之外的其他人可以看到它)。

    • 公开属性为您提供了一种隐藏实现的方法。它还允许您在不更改使用它的代码的情况下更改实现(例如,如果您决定更改数据在类中的存储方式)

    • 许多使用反射处理类的工具只关注属性(例如,我认为一些用于序列化的库以这种方式工作)。始终如一地使用属性可以更轻松地使用这些标准 .NET 工具。

    关于间接费用:

    • 如果 getter/setter 是通常的一行代码,它只是读取/设置字段的值,那么 JIT 应该能够内联调用,因此不会产生性能开销。

    • 当您使用自动实现的属性(C# 3.0 和更高版本)时,语法开销会大大减少,所以我认为这不是问题:

      protected int SomeProperty { get; set; }
      

      事实上,这让您可以非常轻松地将set 保护和get 公开,因此这甚至比使用字段更优雅。

    【讨论】:

      【解决方案4】:

      公共和/或受保护的字段是不好的,因为它们可以在声明类外部未经验证地进行操作;因此可以说它们打破了面向对象编程的封装原则。

      当你失去封装时,你失去了声明类的契约;您不能保证该类的行为符合预期或预期。

      使用属性或方法访问字段使您能够维护封装,并履行声明类的约定。

      【讨论】:

        【解决方案5】:

        我同意只读属性的答案。但是在这里扮演魔鬼的拥护者,这真的取决于你在做什么。我很乐意承认我一直与公众成员一起编写代码(我也不发表评论、遵循指南或任何形式)。

        但当我在工作时,情况就不同了。

        【讨论】:

          【解决方案6】:

          这实际上取决于您的类是数据类还是行为类。

          如果您将行为和数据分开,则可以公开数据类的数据,只要它们没有行为即可。

          如果该类是行为类,则不应公开任何数据。

          【讨论】:

            猜你喜欢
            • 2014-12-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-03-04
            • 2011-08-10
            • 2013-07-19
            • 2013-04-01
            相关资源
            最近更新 更多