Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does IsLiteral return false for decimal?

The following program will print the fields and whether the are constant or not using IsLiteral

public static class Program
{
    public static void Main(string[] args)
    {
        foreach (var field in typeof(Program).GetFields())
        {
            System.Console.WriteLine(field.Name + " IsLiteral: " + field.IsLiteral);
        }

        System.Console.ReadLine();
    }

    public const decimal DecimalConstant = 99M;
    public const string StringConstant = "StringConstant";
    public const int IntConstant = 1;
    public const double DoubleConstant = 1D;
}

It works correctly for all types, except for decimal is will return false.

Can anyone explain this behavior? And is there a better way to see if a field is constant?

like image 837
Rolf de Vries Avatar asked Jul 31 '18 07:07

Rolf de Vries


1 Answers

It's not a constant from a runtime perspective - because the CLR basically doesn't know about decimal; it's not a primitive type. That's why you can't use decimals in attributes, too.

If you look at the IL for the fields, you can see this in action:

.field public static initonly valuetype [mscorlib]System.Decimal DecimalConstant
.custom instance void [mscorlib]System.Runtime.CompilerServices.DecimalConstantAttribute::.ctor
  (uint8, uint8, uint32, uint32, uint32) =
  (01 00 00 00 00 00 00 00 00 00 00 00 63 00 00 00 00 00) 
.field public static literal string StringConstant = "StringConstant"
.field public static literal int32 IntConstant = int32(0x00000001)
.field public static literal float64 DoubleConstant = float64(1.)

Notice how the IL for the other constants does have literal in it, but DecimalConstant doesn't. Instead, it's just a read-only field with an attribute applied. The attribute allows other compilers to treat the field as a constant and know the value - so it can appear in other const expressions, for example.

There's then a type initializer to set the field value at execution time:

.method private hidebysig specialname rtspecialname static 
        void  .cctor() cil managed
{
  // Code size       13 (0xd)
  .maxstack  8
  IL_0000:  ldc.i4.s   99
  IL_0002:  newobj     instance void [mscorlib]System.Decimal::.ctor(int32)
  IL_0007:  stsfld     valuetype [mscorlib]System.Decimal Program::DecimalConstant
  IL_000c:  ret
} // end of method Program::.cctor

Again, this is only present for DecimalConstant because the runtime handles the other fields directly.

like image 132
Jon Skeet Avatar answered Nov 10 '22 19:11

Jon Skeet