【问题标题】:Is it ever useful to have reference type declared as const?将引用类型声明为 const 有用吗?
【发布时间】:2014-01-01 16:54:10
【问题描述】:

为什么有人会这样做:

const object useless = null;
const IEnumerable meaningless = null;

Eric Lippert 说功能在默认情况下是未实现的,每一种可能性都会增加测试、维护等方面的工作量……为什么引用类型的 null 值需要作为常量?

【问题讨论】:

  • 这个“功能”可能不是有意的,它可能只是const 功能工作方式的副作用。我想如果说引用类型上不能有 const 关键字,那将是额外的工作。
  • 我想不出为什么有人会正确编写这样的代码,但是,C# 编译器允许这样做似乎是完全合乎逻辑的,因为null 是引用类型的编译时常量.
  • 在 .Net 4.0、Visual Studio 2010 中,第 2 行无法编译:不能将类型 'IEnumerable' 声明为 const
  • @David 刚刚在 VS 2012 中尝试过。确实如此..
  • 即使是值类型的常量也可能毫无用处:const int Two = 2;。好用与否不取决于语法规则。

标签: c# constants design-decisions


【解决方案1】:

Servy 的观点很好。让我以稍微不同的方式解释这一点。

让我们从更一般的问题开始:“C# 1.0 编译器是否应该将引用类型的空文字分类为常量?”我想强调一下,我们在这里推理的是 C# 1.0,因此您不会考虑任何关于可空值类型或泛型的问题。

那么,将任何事物归类为常数有什么意义?关键是某些语言结构需要常量:

  • case 子句的值
  • 属性
  • 恒定的局部变量和字段

并且常量对可达性分析有影响:

if (0 == 0) 
  M(out x); // always reached, so x is initialized 
else
  M(out y); // unreachable, so y is not initialized.

现在,让我们假设我们接受 null 在属性中很有用,而 case null 虽然有点奇怪,

if (null == null)

应该被视为常量 true。那么您的建议是说null 在这三种方式中都是一个常量,但仍然不能分配给const 本地或字段???这是一个奇怪的限制;如果null 在需要常量的所有其他情况下都可以被视为常量,那么为什么在定义字段或本地时不应将其视为常量?

当然,我还没有回答你的实际问题,即“这什么时候有用?”

好吧,再一次,让我们退后一步。 any 常量何时有用?编译器会处理常量,就好像它们的值被替换到它们的用法中一样,所以你为什么要说:

const int Blah = 0;
...
if (x == Blah)

当你可以简单地说

if (x == 0) 

?当我这样说时,我希望推理是显而易见的。使用任何常量字段或局部变量的原因是为一个值命名,以便代码的读者更好地理解它。空常量字段或本地完全相同。可以说阅读起来更清楚:

if (node != Empty)
    stack.Push(node.Right);

比

if (node != null)
    stack.Push(node.Right);

【讨论】:

  • Eric,我想你已经回答了const 什么时候真正有用这个问题——你最初提到的四种情况。后一种关于可读性的解释,适用于任何变量,尤其是static readonly,但我明白了。
【解决方案2】:

Eric Lippert 说功能在默认情况下是未实现的,每一种可能性都会增加测试、维护等方面的工作量……为什么引用类型的 null 值需要作为常量?

实现“任何实例字段都可以标记为const”的功能比实现“如果有任何非空编译时文字,任何实例字段都可以标记为const”的功能更容易那种类型的”。

您实际上是在提议添加一项功能,即“如果没有该类型的非空编译时文字,则无法将字段标记为const。”该功能默认未实现,如果添加,将增加测试、维护等方面的工作量。

【讨论】:

    【解决方案3】:

    在大多数情况下,我只会说不。
    我能看到类似有用的东西的唯一情况是在预处理器条件内。例如:

    #if DEBUG
    const Object obj = "Debug text";
    #else
    const Object obj = null;
    #endif
    

    【讨论】:

    • 这正是你不应该对const做的事情。 const 不仅仅是同一个程序集中的编译时,它也是所有引用你的程序集的编译时常量。如果不应将obj 视为跨所有程序集的编译时常量,因为它可能取决于将在运行时使用的程序集版本,则它不应为const。 (不过,它可能仍然是 readonly。)
    • @hvd 所以你是说如果我将 null 加载到运行时的另一个版本,它会改变它的含义吗?你什么时候曾经使用 const?我不确定我明白你的意思。非常感谢您提供支持这一点的参考,因此我会深入了解您的话。
    • 不,我是说如果另一个程序集使用obj,并且它是针对您的库的调试版本构建的,但在运行时使用您的库的发布版本,它仍然会“看到" "调试文本"。如果我不期望价值永远改变,我只会使用const,并且如果我的期望是错误的,我愿意处理破损。 (我手边没有参考资料,但如果您愿意,可以稍后查找。)
    • @hvd 哦,我明白了,但很难完全同意。我不希望我的调试和发布程序集混合在一起,更不用说不希望我的调试版本能够发布。此外,假设此代码位于共享库中,我只会部分同意。我个人不会避免这段代码,但你确实提出了一个有效的观点,所以谢谢你。
    【解决方案4】:

    它对于null 具有特殊含义的(通常是递归的)数据结构有意义,而不是(或除了)通常的含义。

    例如,一个集合实现,其中null 表示空集合。将集合实现为二叉搜索树的链表将使这成为一件很自然的事情。在这种情况下,定义 const Set Empty = null 会很有意义。经常与滥用运算符重载以及大量静态方法一起出现。

    这种做法符合理论计算机科学中经常使用的惯例,它可以被视为理论泄漏到实践中。

    这是否是一件好事(tm) 是另一回事,但它确实发生了。

    【讨论】:

      【解决方案5】:

      有一个很常见的sentinel value 模式,其中一些“特殊”实体用作“终止循环”操作。我可以将以下代码视为此类代码的合理常量(请注意,显然样本非常做作,有真正的算法依赖于哨兵):

       const Item ItemLookpupSentinel = null; 
      
       Item Serach(IEnumerable<Item> items)
       {
             var sequenceWithSentinel = 
                items.Concat(Enumerable.Repeat(ItemLookpupSentinel, 1));
             foreach(var item in  sequenceWithSentinel )
             {
                   if (item == ItemLookpupSentinel)
                      return null;
             }
       }
      

      【讨论】:

      • 当我看到它完成时,它总是使用一个 不 引用空引用的static readonly 字段完成。
      【解决方案6】:

      一个有用的引用类型常量的例子是:

      const string UsefulSite = "http://stackoverflow.com";
      

      仅仅因为将null 分配给一个常量就禁止引用类型常量不是很有用,似乎不太合适。值类型常量也可能毫无用处:

      const int FourtyTwo = 42;
      

      充分利用 C# 为您提供的可能性取决于您。

      【讨论】:

      • 这是一个很好的视角。顺便说一句,当然在 q 我的意思是我没有正确措辞的非字符串引用类型。
      • 感谢您的回答,您和Servy的回答完美地回答了它,无法区分。我会接受他非常直接的回答。
      【解决方案7】:

      From comments, as @Brian points out:

      希望能够将 null 视为 const 的一个原因是,您可以提供一个默认为 null 的可选参数(对于引用类型)。这不是一个合理的特性(默认参数在 C# 1.0 中不存在),但它有利于现在允许将引用类型声明为 const(为了保持一致性)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-12-14
        • 1970-01-01
        • 1970-01-01
        • 2018-07-03
        • 1970-01-01
        • 2010-12-28
        • 1970-01-01
        相关资源
        最近更新 更多