问题实际上非常简单.以下代码在其下面引发异常:
class Foo
{
public const StringBuilder BarBuilder = new StringBuilder();
public Foo(){
}
}
Run Code Online (Sandbox Code Playgroud)
错误:
Foo.BarBuilder'的类型为'System.Text.StringBuilder'.除string之外的引用类型的const字段只能用null初始化.
MSDN说这是我理解的,从const视角来说它是有道理的:
常量表达式是一个可以在编译时完全计算的表达式.因此,引用类型常量的唯一可能值是string和null引用.
但是,我没有看到我们使用null常数的原因或原因.那么为什么首先可以定义一个引用类型(除了字符串),const如果它只能被设置为null,如果它是一个故意的决定(我相信它是)那么我们在哪里可以使用常量值和空值?
更新:
当我们想到答案时,请让我们的思维方式不同于"我们有这个,所以为什么不......"背景.
来自MSDN
当编译器在C#源代码中遇到常量标识符(例如,几个月)时,它会将文字值直接替换为它生成的中间语言(IL)代码.由于在运行时没有与常量关联的变量地址,因此const字段不能通过引用传递,也不能在表达式中显示为l值.
因为需要在运行时构造引用类型(除null之外,以及特殊的字符串),所以引用类型不可能实现上述类型.
对于参考类型,您可以获得的最接近的是静态只读:
class Foo
{
// This is not a good idea to expose a public non-pure field
public static readonly StringBuilder BarBuilder = new StringBuilder();
public Foo(){
}
}
Run Code Online (Sandbox Code Playgroud)
与const替换(在调用代码中)不同,static readonly创建引用类型的单个共享实例,如果更改了程序集版本,则该实例具有细微差别.
尽管不能(通常)重新分配引用,但它并不排除在StringBuilder(如Appendetc)上调用非纯方法.这是不同的consts,其中值类型和字符串是不可变的(并且可以说应该是"永恒的").
However, I don't see the reason why or where we would use null constant.
Null constants are useful as sentinel values.
For example, this:
public class MyClass
{
private const Action AlreadyInvoked = null;
private Action _action;
public MyClass(Action action) {
_action = action;
}
public void SomeMethod()
{
_action();
_action = AlreadyInvoked;
}
public void SomeOtherMethod()
{
if(action == AlreadyInvoked)
{
//...
}
}
}
Run Code Online (Sandbox Code Playgroud)
Is much more expressive than this:
public class MyClass
{
//...
public void SomeMethod()
{
_action();
_action = null;
}
public void SomeOtherMethod()
{
if(action == null)
{
//...
}
}
}
Run Code Online (Sandbox Code Playgroud)
The source code for the Lazy<T> class shows Microsoft used a similar strategy. Although they used a static readonly delegate that can never be invoked as a sentinel value, they could have just used a null constant instead:
static readonly Func<T> ALREADY_INVOKED_SENTINEL = delegate
{
Contract.Assert(false, "ALREADY_INVOKED_SENTINEL should never be invoked.");
return default(T);
};
Run Code Online (Sandbox Code Playgroud)