如果我们只能为它们分配null,为什么我们允许使用带引用类型的const?

Tar*_*rik 10 c# const

问题实际上非常简单.以下代码在其下面引发异常:

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,如果它是一个故意的决定(我相信它是)那么我们在哪里可以使用常量值和空值?

更新:

当我们想到答案时,请让我们的思维方式不同于"我们有这个,所以为什么不......"背景.

Stu*_*tLC 6

来自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,其中值类型和字符串是不可变的(并且可以说应该是"永恒的").


dca*_*tro 6

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)