【问题标题】:Why can constants be implicitly converted while static readonly fields cannot?为什么可以隐式转换常量而静态只读字段不能?
【发布时间】:2018-06-30 22:45:31
【问题描述】:

鉴于下面的代码,我想知道为什么referenceValue = ConstantInt; 有效而referenceValue = StaticInt; 无法编译。

namespace Demo
{
    public class Class1
    {
        private const int ConstantInt = 42;
        private static readonly int StaticInt = 42;

        public void DemoMethod(ref uint referenceValue)
        {
            referenceValue = ConstantInt; // This compiles
            referenceValue = StaticInt; // This claims that the source type 'int' cannot be converted to 'unit' implicitly. 
        }
    }
}

【问题讨论】:

标签: c# static constants


【解决方案1】:

常量在编译时被它们各自的值替换。所以从编译器的角度来看,这个referenceValue = ConstantInt; 和这个referenceValue = 42 是一样的。

虽然readonly 字段感觉相似,但事实并非如此。它们的价值在编译时并不真正知道。它们由类上的静态字段支持。它们的值可以计算,甚至可以从静态构造函数修改,因此编译器无法在编译时检查该值是否在uint 的范围内。

例如:

public class Class1
{
    private const int ConstantInt = 42;
    private static readonly int StaticInt = 42;

    static Class1()
    {
        StaticInt = -20;
    }

    public void DemoMethod(ref uint referenceValue)
    {
        referenceValue = StaticInt; // it's -20
    }
}

编辑

正如 cmets 中指出的,并非所有从常量到变量的赋值都有效,long 常量到 int 变量在没有显式转换的情况下不起作用。根据常量的类型,这种行为是相同的,无论它是命名常量还是内联常量:

private const long ConstantInt = 42;
// Both of these will cause an error:
referenceValue = ConstantInt; // can't be converted implicitly
referenceValue = 42L; // but neither can an inline long constant (the L makes it long)

【讨论】:

  • 所以从编译器的角度来看,referenceValue = ConstantInt;referenceValue = 42; 相同 不正确,因为const long ConstantInt = 42 会出错。 不领先!类型仍然是。问题是编译器可以进行一些转换,而有些则不能。在这方面,它比运行时可以做的更进一步。
  • @PatrickHofman 你是对的,这是一个过度简化,我的意思是常量实际上会被它在 IL 中的值替换,关于转换,你是对的,不是任何转换都可以工作,但是这更多地取决于管理分配的规则:referenceValue = 20L 不起作用,referenceValue = 20u 起作用
  • 明确一点,规则是int常量可以转换成sbyte、byte、short、ushort、uint或者ulong,如果值合适的话,long常量可以转换成ulong如果合适的话。通常 C# 不允许这些转换,但它对已知适合的常量例外。但是,即使它适合,您也不能将 long 常量放入 int 中;这可能是一个错误,因此 C# 将其标记为这样。
  • 编译器在编译时无法检查该值是否在 uint 范围内。 实际上它不能总是检查该值。它可以检查是否有这样的静态构造函数/计算,如果没有,则允许赋值,即使有,它也可以尝试执行一些静态分析来证明......显然最后的分析可能并不总是有效。跨度>
【解决方案2】:

因为常量字段是在编译时评估的,而readonly 字段是在运行时评估的。编译器中的解释器对整数的处理方式与运行时不同。

编译器识别出该值及其类型,并可以基于此进行一些基本的转换,就像在本例中所做的那样。如果将ConstantInt 设置为负数,请尝试看看会发生什么。编译器会出错。将类型更改为longfloat 时也是如此:没有编译器转换规则,所以也会出错。

【讨论】:

    【解决方案3】:

    来自doc

    readonly 关键字与 const 关键字不同。 const 字段只能在字段声明时进行初始化。只读字段可以在声明或构造函数中初始化。因此,根据使用的构造函数,readonly 字段可以具有不同的值。此外,虽然 const 字段是编译时常量,但 readonly 字段可用于运行时常量

    如这一行:public static readonly uint l1 = (uint)DateTime.Now.Ticks;

    【讨论】:

      猜你喜欢
      • 2017-12-08
      • 1970-01-01
      • 2016-08-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-19
      • 1970-01-01
      相关资源
      最近更新 更多