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

时间:2014-12-30 05:11:17

标签: c# const

问题实际上非常简单。以下代码抛出了它下面的异常:

class Foo
{
    public const StringBuilder BarBuilder = new StringBuilder();
    public Foo(){

    }
}

错误:

  

Foo.BarBuilder'属于' System.Text.StringBuilder'。一个const字段   除了字符串之外的引用类型只能用   空。

MSDN说这是我理解的,从const角度来看是有道理的:

  

常量表达式是一个可以在中完全计算的表达式   编译时间。因此,常量的唯一可能值   引用类型是字符串和空引用。

但是,我没有看到我们使用null常数的原因或位置。那么为什么首先可以使用const定义引用类型(除了字符串),如果它只能设置为null并且是否是故意的决定(我相信它是)那么我们在哪里可以使用常数和空值?

更新

当我们想到答案时,请让我们以不同的方式思考#34;我们有这个,所以为什么不这样做......"上下文。

4 个答案:

答案 0 :(得分:6)

来自MSDN

  

当编译器在C#源代码中遇到常量标识符(例如,几个月)时,它会将文字值直接替换为它生成的中间语言(IL)代码。因为在运行时没有与常量关联的变量地址,所以const字段不能通过引用传递,也不能在表达式中显示为l值。

因为引用类型(除null之外,以及are special)需要在运行时构造,所以上述引用类型是不可能的。

对于参考类型,您最接近的是static readonly

class Foo
{
    // This is not a good idea to expose a public non-pure field
    public static readonly StringBuilder BarBuilder = new StringBuilder();
    public Foo(){
    }
}

与const替换(在调用代码中)不同,static readonly创建一个引用类型的共享实例,如果更改了程序集版本,则该实例具有subtle differences

虽然引用不能(normally)被重新分配,但它并不排除在StringBuilder上调用非纯方法(如Append等)。这与consts不同,其中值类型和字符串是不可变的(可以说应该是"eternal")。

答案 1 :(得分:5)

  

但是,我没有看到我们使用null常量的原因或原因。

空常量对sentinel values很有用。

例如,这个:

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)
        {
            //...
        }
    }
}

比这更具表现力:

public class MyClass
{
    //...

    public void SomeMethod()
    {
        _action();

        _action = null;
    }

    public void SomeOtherMethod()
    {
        if(action == null)
        {
            //...
        }
    }
}

Lazy<T>类的源代码显示Microsoft使用了类似的策略。虽然他们使用了永远不能作为标记值调用的静态只读委托,但他们可能只使用了一个空常量:

static readonly Func<T> ALREADY_INVOKED_SENTINEL = delegate
{
    Contract.Assert(false, "ALREADY_INVOKED_SENTINEL should never be invoked.");
    return default(T);
};

答案 2 :(得分:0)

正如您在问题中所述,有一种引用类型可以放入const引用 - 字符串。编译器特殊情况并将字符串放入编译输出中,并允许它们在运行时读入引用类型。

当然这引出了一个问题 - 为什么不将字符串作为唯一可以const的引用类型,只要我们特别包装它们呢?对此,我只能推测在编译器中添加特殊情况比在语言中添加特殊情况更简单且更少问题。从语言的角度来看,字符串只是一种引用类型,即使编译器具有从字符串文字和编译资源创建它的实例的特殊处理。

答案 3 :(得分:0)

我认为你问为什么带有null的引用类型允许作为常量。

我认为你是对的,它没有多大意义但是如果你设计了自己的库并且想要与null进行比较但是想要给出特殊意义(比如只与你的库值比较)那么它是有用的直接null)

public class MyClass
    {
        public const MyClass MyClassNull = null;
        public MyClass()
        {
        }
    }
像这样使用它。

object obj = GetMyClass();
if(obj == MyClass.MyClassNull) // This going to convert to actual null in MSIL.
{    
}