简体   繁体   中英

Why can constants be implicitly converted while static readonly fields cannot?

Given the below code, I wonder why referenceValue = ConstantInt; is valid while referenceValue = StaticInt; fails to compile.

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. 
        }
    }
}

Constants are replaced at compile time with their respective value. So from the compiler perspective this referenceValue = ConstantInt; is the same as this referenceValue = 42 .

While readonly fields feel similar, they are not. Their value is not truly known at compile time. They are backed by a static field on the class. Their value can be computed, even modified from a static constructor, so the compiler can't check that the value is within the range of uint at compile time.

For example:

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
    }
}

Edit

As pointed out in the comments not all assignments from constants to variables work, long constant to int variable does not work without an explicit cast. This behavior is the same based on the type of the constant, regardless of whether it is a named constant or an inline constant:

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)

Because constant fields are evaluated on compile time, while readonly fields are evaluated on run time. The interpreter in the compiler treats your integers differently than the runtime does.

The compiler recognizes the value and its type and it can do some basic conversion based on that, as it does in this case. Try and see what happens if you set ConstantInt to a negative number. The compiler will error out. The same is true when you change the type to long or float : there is no compiler conversion rule, so it errors out too.

from the doc

The readonly keyword is different from the const keyword. A const field can only be initialized at the declaration of the field. A readonly field can be initialized either at the declaration or in a constructor. Therefore, readonly fields can have different values depending on the constructor used. Also, while a const field is a compile-time constant, the readonly field can be used for runtime constants

as in this line: public static readonly uint l1 = (uint)DateTime.Now.Ticks;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM