Decimal.MinValue和Decimal.MaxValue:为什么要使用静态只读而不是常量修饰符?

8
在C#中,对于带有数字类型的MinValue字段定义为:

① 对于十进制类型,使用static readonly修饰符 (链接到.NET 4.5的MSDN库)

public static readonly decimal MinValue

② 对于所有其他数值类型,使用const修饰符:

//Integral signed numeric types
public const sbyte MinValue
public const short MinValue
public const int MinValue
public const long MinValue

//Integral unsigned numeric types
public const byte MinValue
public const ushort MinValue
public const uint MinValue
public const ulong MinValue

//Real numeric types
public const float MinValue
public const double MinValue

为什么不使用“const”修饰符来定义Decimal.MinValue字段?
备注:
① 同样的问题也适用于数值类型的MaxValue字段。
② VB、C++和F#也使用不同的修饰符来定义十进制类型,因此这个问题并不特定于C#语言。

1
Decimal 不像其他类型那样原始。 - Mark Hurd
2
请注意,无论在源代码中需要使用什么技巧使其成为可能,Decimal 结构体的 MinValue 字段一个 const 并且像一个常量一样运作。如果您使用 Visual Studio 并在 decimal 上使用“转到定义”(F12),则会看到“元数据”(通过反射从实际程序集生成的 C# 代码)显示 [DecimalConstant(0, 128, 4294967295, 4294967295, 4294967295)] public const decimal MinValue = -79228162514264337593543950335m;。因此,MinValue 的作用类似于一个常量。您可以从它声明其他 const 值,将其用作可选参数的默认值等。 - Jeppe Stig Nielsen
@MarkHurd 在.NET中这是正确的,但在C#规范中,类型decimal是语言中的预定义类型,它是C#中所谓的简单类型,详见4.1值类型4.1.4简单类型(这里我链接了旧版本的规范,但最新版本也是一样的)。而且C#允许所有这些“简单类型”,包括decimal,使用const字段。现在当mscorlib包含一个“预定义类型”的源代码时,可能需要一些技巧。 - Jeppe Stig Nielsen
1
所以,实际上这个问题已经不再相关了,因为在实际代码中它是一个“const”,而不是文档中所述的“static readonly”。但是,这个问题仍然非常深奥。 - theMayer
4个回答

2

这是一个有趣的问题。我进行了一些研究,关于 Decimal 最小/最大字段的 MS 文档明确表示(注意两者用语相同,为了清晰起见在括号中显示):

该常数的值为 [负] 79,228,162,514,264,337,593,543,950,335。

以下代码可以编译通过:

public const decimal MaxValue = 79228162514264337593543950335M;

- 编辑 -

注:这里是.NET 4.5的源代码分配字段(从微软下载的PDB源代码),由此代码调用的十进制构造函数。请注意,它声明了一个常量值。至少在4.5中,文档似乎是错误的(这不是微软文档第一次出错)。另外,正如@Daniel在评论中指出的那样,源代码似乎无法编译。

public const Decimal MinValue = new Decimal(-1, -1, -1, true, (byte) 0);
public const Decimal MaxValue = new Decimal(-1, -1, -1, false, (byte) 0);

public Decimal(int lo, int mid, int hi, bool isNegative, byte scale)
{
  if ((int) scale > 28)
    throw new ArgumentOutOfRangeException("scale", Environment.GetResourceString("ArgumentOutOfRange_DecimalScale"));
  this.lo = lo;
  this.mid = mid;
  this.hi = hi;
  this.flags = (int) scale << 16;
  if (!isNegative)
    return;
  this.flags |= int.MinValue;
}

同时需要注意的是:在2.0框架中,decimal被直接声明:

public const Decimal MaxValue = 79228162514264337593543950335m;

因此,我得出的结论是不一致和错误的文档。我将留给其他人去查看其他框架版本是否存在模式。


为什么有const而不是readonly,这是反编译器的问题吗?使用const关键字编译此代码会失败。 - Daniil Grankin
好的观点 - 这必须是特定于版本的。2.0框架明确表示public const Decimal MaxValue = 79228162514264337593543950335m; - theMayer
在 .net 中,无法将以下代码用于编写程序:public const Decimal MaxValue = new Decimal(-1, -1, -1, false, (byte) 0);,因为常量的初始值必须是编译时常量而不是构造函数的调用。清楚吗? - Daniil Grankin
可能微软的源代码有误?这不是反编译,而是从微软源服务器下载的。 - theMayer
1
哦,我不知道。对于MinValue和MaxValue,它们带有奇怪的注释“此字段是常量且只读”。我曾经认为它们是不同的东西 :) - Daniil Grankin
显示剩余2条评论

1
首先,需要注意的是,从语言的角度来看(而不是CLR),这些Decimal值被视为常量,正如Jon Skeet在他对rmayer06相关问题的回答中所提到的那样。请注意,涉及编程方面的内容。
(我原以为使用ReadOnly而不是Const的原因是为了避免在每次使用Const时调用构造函数,但实际上并非如此 :-()。
如果在C#或VB.NET中编译:x == Decimal.MaxValue,则该常量将被构造,就好像它真的是一个Const
在VB.NET中,这对于Decimal.OneDecimal.ZeroDecimal.MinusOne并非如此,但在C#中,这些都被视为常量。(顺便说一句,String.Empty在任一语言中都不被视为常量。)
因此,我认为这些都是作为常量内置到语言中的(有时,在VB的情况下)。

在VB的情况下,它似乎更喜欢加载ReadOnly值,除了MaxValueMinvalue。(也就是说,Const y1 As Decimal = Decimal.One 在VB中被有效地别名,但在C#中真正被视为常量。)

顺便说一句,Date.MaxValue也是ReadOnly,但在VB.NET中不被视为常量(即使它确实有Date文本)。

更新:我没有检查最新的VB.NET或C#的结果是什么,但参考源代码并不特别处理Decimal.OneDecimal.ZeroDecimal.MinusOneDecimal.MaxValueDecimal.MinValue;它们都只是{{link1:public const}}。


1

尽管MSDN library将十进制数的MinValue字段描述为static readonly,但C#编译器将其视为const

如果MinValue字段是只读的,下面的代码将无法编译,但实际上可以编译

const decimal test = decimal.MinValue - decimal.MinValue;

备注:

① 相同的答案适用于十进制类型的MaxValue字段。

② 有关更多详细信息,Jon Skeet在here中提供了关于IL级别常量字段实现的见解,其中包括:

  • 原始数字类型(整数、浮点数和双精度)和
  • 非原始数字类型十进制类型。

0

我认为这是为了提高源代码的可读性。

public const Decimal MaxValue = new Decimal(-1, -1, -1, false, (byte) 0);

看起来比魔数更易读:

public const Decimal MaxValue = 79228162514264337593543950335m

我不知道更易读,但肯定更清楚数字来自哪里。 - theMayer

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