【问题标题】:Mimicing constants using static readonly vs static getter使用静态只读与静态 getter 模拟常量
【发布时间】:2016-06-21 22:14:31
【问题描述】:

我正在使用第三方控件,它使用“奇怪的值”来区分选项列表。他们使用两个不同的属性来唯一标识每个选择。

Example:
"Field" + "RW" = "CheckedOutBy"
"System" + "N" = "Name"
"Field + "N" = "Notifier"

总共有 37 个不同的选项(每个选项都有两个值的不同组合,构成 37 个唯一选项)。

我创建了一个存储两个值的结构,我的想法是为每个选择创建一个新的结构实例。

public struct ColumnCode : IEquatable<ColumnCode>
{
    public static readonly ColumnCode Empty = new ColumnCode();

    private readonly ColumnType _columnType;
    private readonly string _code;

    internal ColumnCode(ColumnType columnType, string code)
    {
        _columnType = columnType;
        _code = code;
    }

    public override string ToString() { ... }
    public bool Equals(ColumnCode other) { ... }
    public override int GetHashCode() { ... }
}

理想情况下,我想为每个选择创建一个“常量”,但由于常量不是一个选项,我想尝试模仿一个常量。

我想出的两种方法是使用静态只读字段或仅使用 getter 的静态属性。

public static class FieldOption 
{
    public static ColumnCode CheckedOutBy { get; } = new ColumnCode(ColumnType.Field, "XW");
    public static ColumnCode Name { get; } = new ColumnCode(ColumnType.System, "N");
    public static ColumnCode Notifier { get; } = new ColumnCode(ColumnType.Field, "N");
}

public static class FieldOption 
{
    public static readonly ColumnCode CheckedOutBy = new ColumnCode(ColumnType.Field, "XW");
    public static readonly ColumnCode Name= new ColumnCode(ColumnType.System, "N");
    public static readonly ColumnCode Notifier = new ColumnCode(ColumnType.Field, "N");
}

无论哪种情况,我现在都可以使用FieldOption.CheckedOutByFieldOption.NameFieldOption.Notifier 等来引用我的C# 代码中的选项,但我不确定一种方法是否比另一种更好。

这些选择中的一个是否比另一个更适合模仿 const,或者是否有更好的方法我没有考虑。

我在互联网上阅读了大量信息,但仍然没有找到一个好的答案。其中一些似乎是矛盾的。许多信息表明属性优于字段,但在这篇文章 (https://msdn.microsoft.com/en-us/library/ms229057(v=vs.110).aspx) 中,微软说“对预定义的对象实例使用公共静态只读字段”所以我觉得静态只读字段是正确的选择。

我也不确定反射是如何在这里发挥作用的。我想确保 FieldOptions 的值不能更改,即使通过反射也是如此。

对此的任何帮助将不胜感激。

【问题讨论】:

  • 你知道关键字的作用吗?
  • @Jeroen Vannevel 不确定我是否理解您的问题?我只是想知道 C# 编译器是否以不同的方式处理这些?是否有性能优势等
  • 一个是静态的,另一个不是。一个是属性,另一个是字段。显然这些会有差异 - 由您决定您对哪种差异感兴趣。如果您的意思是所有差异,那么最好的做法是阅读文档并查看效果是static 以及字段和属性的不同之处。在您提出更具体的问题后,我们可以改为回答
  • 到目前为止,帖子中没有任何迹象表明问题比我作为重复链接的常规“公共字段与属性”主题更具体......我相信你之前已经仔细阅读过提出问题,但未反映在帖子中 - 如果您认为需要重新打开帖子,请确保编辑您的帖子。
  • @Phaeze 性能问题应该包含测量、性能目标和关于作者希望看到什么解释/改进的具体问题。这也是对展示研究的常规要求的补充(尤其是在诸如“财产访问是否被内联”之类的问题上讨论到死)......所以我怀疑 OP 是否真的在问这个问题(因为帖子中没有显示任何必需的信息)

标签: c#


【解决方案1】:

我想确保 FieldOptions 的值不能改变,即使是通过反射

实现这一点的唯一方法是使用只读属性,即使这样,也只有在每次调用 getter 时都重新实例化相应的值,例如:

public static ColumnCode CheckedOutBy
{
    get { return new ColumnCode(ColumnType.Field, "RW"); }
}

使用您显示的语法,或使用私有设置器的自动属性,仍然有一个支持字段,并且该支持字段仍然可以通过反射更改,尽管由于字段名称在源代码,并具有依赖于编译器实现的名称。

当然,每次调用 getter 时都重新实例化该值会对性能产生负面影响。因此,将属性设置为深度只读(即不受反射)是有代价的。这种性能成本在您的方案中是否重要,您必须自己确定。这个问题没有“一个正确的答案”。

我在互联网上阅读了大量信息,但仍然没有找到一个好的答案。其中一些似乎是相互矛盾的

这是因为,除了您有一些特定约束来告知您的决定(例如希望防止反射能够更改值)的情况之外,这实际上主要是个人喜好问题。 The previously proposed duplicate answer 解决了许多将字段与属性区分开来的问题,但实际上,在您询问的特定场景中,几乎没有真正的区别。

字段名义上表现更好,因为它们可以直接访问而不需要方法调用。但是 a) 在许多情况下,方法的主体将被内联,从而抵消了该优势,并且 b) 即使存在方法调用,在大多数情况下,它的开销也不足以影响甚至是可测量的。


因此,您需要决定:您需要对通过反射修改这些值的代码提供多大程度的保护?是否真的足够重要以至于值得用程序生成的值包装属性中的值?如果这样做,您将多久访问一次这些属性?每次调用 getter 时按需生成值的开销是否会影响代码的实用性?

这些问题只有你能回答。这里的人不可能为你回答这个问题。

【讨论】:

  • 你不能真正争辩说某人会“不小心”去使用反射来修改私有支持字段的值,而没有意识到他们不是故意这样做的,并且是确定的恶意运行完全特权代码的用户已经是一个失败的原因。这两者之间真的没有任何关系。
  • 他们故意违反了代码的约束。他们很清楚,他们不应该改变价值,而且无论如何他们都在这样做。这样做时,他们需要为违反该库的明确期望的后果做好准备。如果更改的结果是代码不起作用,他们破坏了自己的代码,没有理由阻止他们。如果它允许他们做一些他们不做的重要事情,那么无论你做什么,你都无法阻止他们。如果他们只想破坏自己的程序,那就让他们吧。
  • 是的,完全正确。我不明白使用反射来设置私有匿名支持字段会如何“意外”或“粗心”地做一些你认为该代码的作者希望你能够做的事情。正如您刚才所说,readonly 就是一个很好的例子。如果你想设置一个readonly 字段,但因为它被标记为“只读”,你知道你不应该这样做,所以如果你想让它工作,你不应该这样做。同样,如果一个属性有一个 getter,而没有(公共)setter,你知道你不应该能够公开设置该值。
  • 所以你是说你的观点是关于阻止人们做可能伤害他们的事情的实际程度是相关的,但没有其他人的观点是相关的,因为这不是问题的实质问?如果您根本不回答这个问题,而只是就您认为他应该做什么(不管他问什么)发表您的意见,那么您为什么反对其他人讨论该行动的适当性?
  • 至于你的比喻,这里并不适用。锁上你的门只不过是告诉看到它的人你宁愿他们不要进入你的房子。你并没有阻止任何人真正进入。这类似于使用只有一个 getter 的属性。您清楚地告知代码用户他们只应该读取该值,并且他们需要做一些他们知道他们不应该做的事情才能尝试设置它。设置一个字段有多难在这里并不是真正的问题,而是他们是否可以在没有意识到他们不应该设置的情况下设置它。
猜你喜欢
  • 2010-10-19
  • 1970-01-01
  • 2014-01-22
  • 1970-01-01
  • 2014-07-02
  • 1970-01-01
  • 1970-01-01
  • 2013-04-16
相关资源
最近更新 更多