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.
The const (read: constant) keyword in C# is used to define a constant variable, i.e., a variable whose value will not change during the lifetime of the program. Hence it is imperative that you assign a value to a constant variable at the time of its declaration.
A const variable is one whose value cannot be changed. A static variable is one that is available from the class itself rather than any object (these are used like global variables for most purposes). A const value has no memory management overhead, since it never needs to be collected.
String constants, also known as string literals, are a special type of constants which store fixed sequences of characters. A string literal is a sequence of any number of characters surrounded by double quotes: "This is a string."
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);
};
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With