Tarik
Tarik

Reputation: 81791

Why are we allowed to use const with reference types if we may only assign null to them?

The question is actually very straightforward. The following code throws the exception right below it:

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

    }
}

Error:

Foo.BarBuilder' is of type 'System.Text.StringBuilder'. A const field of a reference type other than string can only be initialized with null.

MSDN says this, which I understand and it makes sense from const perspective:

A constant expression is an expression that can be fully evaluated at compile time. Therefore, the only possible values for constants of reference types are string and a null reference.

However, I don't see the reason why or where we would use null constant. So why in the first place that a reference type (other than string) can be defined with const if it can be only set to null and if it was a deliberate decision (which I believe it is) then where can we use constant with null values?

Update:

When we think of an answer, please let's think differently than "We have this so why not that..." context.

Upvotes: 13

Views: 4945

Answers (4)

StuartLC
StuartLC

Reputation: 107367

From MSDN

when the compiler encounters a constant identifier in C# source code (for example, months), it substitutes the literal value directly into the intermediate language (IL) code that it produces. Because there is no variable address associated with a constant at run time, const fields cannot be passed by reference and cannot appear as an l-value in an expression.

Because reference types (other than null, and strings which are special) need to be constructed at run time, the above would not be possible for reference types.

For reference types, the closest you can get is 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(){
    }
}

Unlike const substitution (in the calling code), static readonly creates a single shared instance of the reference type which has subtle differences if assembly versions are changed.

Although the reference cannot (normally) be reassigned, it doesn't preclude calling non-pure methods on the StringBuilder (like Append etc). This is unlike consts, where value types and strings are immutable (and arguably should be "eternal").

Upvotes: 7

dcastro
dcastro

Reputation: 68750

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

Is much more expressive than this:

public class MyClass
{
    //...

    public void SomeMethod()
    {
        _action();

        _action = null;
    }

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

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);
};

Upvotes: 7

dotnetstep
dotnetstep

Reputation: 17485

I think that you are asking that why reference type with null allow as a constant.

I think you are right that it does not make much sense but it is useful if you have designed your own library and if you want to compare with null but want to give special meaning ( like comparing with your library value only rather then directly null)

public class MyClass
    {
        public const MyClass MyClassNull = null;
        public MyClass()
        {
        }
    }

it usage like this.

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

Upvotes: 0

Avner Shahar-Kashtan
Avner Shahar-Kashtan

Reputation: 14700

As you state in the question, there is one reference type that can be put into a const reference - strings. The compiler special-cases this and puts the strings into the compiled output and allows them to be read into the reference type at runtime.

Of course this begs the question - why not have strings be the only reference types that can be const, as long as we're special-casing them anyway? To that, I can only speculate that adding a special case in the compiler is simpler and less problematic than adding a special case in the language. From a language perspective, a string is just a reference type, even if the compiler has special handling to create instances of it from string literals and compiled resources.

Upvotes: 1

Related Questions