将公共只读字段用于不可变结构而不是私有字段/公共getter对

时间:2014-07-18 12:01:44

标签: c# .net field readonly getter-setter

这是我第一次编写将用于广泛几何计算的小型不可变结构。我很想使用public readonly字段而非private field/public getter组合。

public struct Vector4
{
    public readonly float Angle, Velocity;

    // As opposed to:
    private float _Angle, _Velocity;
    public float Angle { get { return (this._Angle); } }
    public float Velocity { get { return (this._Velocity); } }

    public Vector4 (float angle, float velocity)
    {
        // Once set in the constructor, instance values will never change.
        this.Angle = angle;
        this.Velocity = velocity;
    }
}

它看起来更清洁并且消除了额外的层(吸气剂)。如果不使用公共领域是不好的做法,那么以这种方式使用公共只读字段是否有任何负面影响?

注意,我们只讨论价值类型。例如,数组会通过调用代码来覆盖要覆盖的元素。

更新

感谢所有输入。对于没有使用数据绑定等的情况,使用public readonly字段似乎没有任何缺点。在我的基准测试中,执行时间下降了70%,这是一个大问题。针对.NET 4,我原本期望编译器内联getter-only属性。基准测试当然是在发布配置中测试的,没有附加调试器。

4 个答案:

答案 0 :(得分:7)

在没有反射的纯C#中,没有理由避免在你的情况下使用只读字段,我可能会自己选择只读字段。属性的大多数一般优点在这里并不真正适用。那说......

任何使用反射来获取属性列表并对这些属性起作用的东西都不能用于字段(无论是否为只读)而不进行修改。

特别是,将属性更改为字段可能会导致数据绑定停止工作。它将继续编译而没有任何问题,但它将不再按照您的意愿执行。如果您有任何此类代码,或者您预计将来会使用此类代码,则需要继续使用属性。

答案 1 :(得分:6)

指南,例如"首选公共字段的公共属性"是准则,而不是规则。

某些案例中,使用字段的 pros 可能会超过 cons 。事实上,Rico Mariani在描述一个这样的场景方面做得很好,在我看来这看起来很像你的场景:

他使用字段而不是属性的主要论点是PointVectorVertex这样的原语通常没有非法值,因此很少或根本不需要添加getter / setter图层。

他也为 mutable 字段提出了很好的论据,但不管怎么说,这不是你的理由。

但是我想自己添加一个观点供您考虑:您的课程是否会被用于数据绑定?数据绑定仅适用于属性,而不适用于字段。

答案 2 :(得分:0)

自动属性可能会有所帮助。

public struct Vector4
    {
    public float Angle { get; private set; }
    public float Velocity { get; private set; }

    public Vector4(float angle, float velocity) : this()
        {
        // Once set in the constructor, instance values will never change.
        this.Angle = angle;
        this.Velocity = velocity;
        }
    }

[注意:更新以使其编译 - 感谢Lasse V. Karlsen指出这一点]

请注意,这与具有支持字段的属性完全相同,只是您让编译器选择其名称(这将是一些'不可言说'的字符串)。

有什么困扰你的财产?如果是详细程度,则使用上面的自动属性。如果您担心性能,那么您是否测量了属性与字段的影响?

有趣的是,在他的书“ CLR via C#”中,杰弗里·里希特在属性方面相当“失望”,声称他希望他们没有被列入CLR。他说他更喜欢明确使用getXXX和setXXX方法。他认为属性语法令人困惑。就个人而言,看到Java代码完全相同,我更喜欢属性语法。

我认为值得一提的是,readonly也可能令人困惑。在这种情况下,它是明确的,因为我们正在处理整个价值类型,readonly'做它在锡上所说的'。但是,如果这些是引用类型,那么readonly仅保护引用 - 也就是说,字段必须始终引用相同的对象,但如果写入允许引用的对象本身可能会发生变异。我怀疑许多没有经验的程序员被这种微妙的绊倒了。

鉴于您测量的性能优势,我认为您对使用公共只读字段有合理的论据,这是一个判断调用。权衡的是,您允许您的类型的消费者与您的结构的内部紧密耦合,基本上牺牲了封装。对于在受控条件下具有经过验证的性能优势的这种简单类型,这可能是合理的。

答案 3 :(得分:0)

.NET Framework设计指南say about fields

请勿提供公共或受保护的实例字段。

这是一个强有力的声明。这不是绝对的规则,但是按照准则的introduction

在某些情况下,好的库设计要求您违反这些设计准则。这种情况应该很少见,重要的是您有一个明确而令人信服的决定理由。

那么这对您的代码意味着什么?一个关键问题是代码的受众。您是否正在按照指南的定义编写“框架”,它是“扩展.NET Framework并与之交互的库之一”?

如果您正在编写框架,则会遇到大多数代码所没有的约束。许多其他代码将取决于您的代码,它们对更改的更改很敏感,并且需要与您的DLL具有二进制兼容性。这就是属性对于框架很重要的原因。如果更高版本的框架需要在getter或setter中添加逻辑,则从字段更改为属性会破坏二进制兼容性。

大多数代码不是框架的一部分。依赖关系很少,如果有简单的重大更改,则可以轻松修复。例如,相关代码可能在outref参数中使用一个字段。如果稍后将该字段更改为属性,以便可以具有一些逻辑,则修复损坏的代码通常没什么大不了的。编译器将引导您解决问题。同样,也不需要二进制兼容性。在不重新编译依赖关系的情况下,您将永远不会放入更新的DLL。

对于非框架代码,如果以后将其更改为属性的机会很小,则最好使用一个字段。字段更灵活,属性更丰富,有时甚至更快。