为什么常量可以隐式转换而静态只读字段不能?

22

对于以下代码,我想知道为什么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. 
        }
    }
}

2
相关 https://dev59.com/S3E85IYBdhLWcg3w_I38 - Cee McSharpface
3个回答

20

常量在编译时被替换为其相应的值。因此,从编译器的角度来看,这个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
    }
}

编辑

正如评论中指出的那样,并不是所有从常量赋值给变量的操作都有效,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)

1
从编译器的角度来看,referenceValue = ConstantInt;referenceValue = 42; 是相同的。但事实并非如此,因为 const long ConstantInt = 42 会出错。值不是主导因素!类型仍然是。问题在于编译器可以进行一些转换,而有些则不能。在这方面,它比运行时更进一步。 - Patrick Hofman
@PatrickHofman 你是正确的,这是一种过度简化,我指的是常量实际上将在 IL 中被它的值替换,关于转换,你是正确的,并非所有的转换都可以使用,但这更多地受到指导赋值的规则的影响:'referenceValue = 20L' 不起作用,'referenceValue = 20u' 起作用。 - Titian Cernicova-Dragomir
2
只是为了明确,规则是如果值适合,则int常量可以转换为sbyte、byte、short、ushort、uint或ulong,而long常量可以转换为ulong。通常情况下,C#不允许这些转换,但对于已知适合的常量,它会做出例外。但是,即使适合,也不能将长常量放入int中;这很可能是一个错误,因此C#会标记它。 - Eric Lippert
编译器无法在编译时检查值是否在uint范围内。实际上,它并不总是能够检查该值。它可以检查是否有这样的静态构造函数/计算,并且如果没有,则允许赋值,即使存在这些函数,它也可以尝试执行一些静态分析来证明...显然,这种最后的分析可能并不总是有效。 - Bakuriu

11
因为常量字段在编译时进行评估,而readonly字段在运行时进行评估。编译器中的解释器与运行时处理整数的方式不同。
编译器识别该值及其类型,并根据此进行一些基本转换,就像在此示例中所做的那样。尝试将ConstantInt设置为负数,看看会发生什么。编译器会报错。当您将类型更改为longfloat时,情况也是如此:没有编译器转换规则,因此它也会报错。

2

来自文档

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

如下代码:public static readonly uint l1 = (uint)DateTime.Now.Ticks;


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接