0

Why would someone ever do:

const object useless = null;
const IEnumerable meaningless = null;

Eric Lippert says features are unimplemented by default and every possibility adds more effort in testing, maintaining etc... Why would a null value for a reference type ever needed as a constant?

nawfal
  • 70,104
  • 56
  • 326
  • 368
  • 2
    This "feature" probably wasn't intended, it was probably just a side effect of how the `const` feature works. I imagine it would be extra work to say that you cannot have a `const` keyword on a reference type. – Matthew Dec 13 '13 at 19:44
  • 2
    I can think of no reason why anyone would right code like that, however, it seems perfectly logical that the C# compiler would allow this, since `null` is a compile-time constant of a reference type. – p.s.w.g Dec 13 '13 at 19:45
  • 3
    In .Net 4.0, Visual Studio 2010, the 2nd line doesn't compile: The type 'IEnumerable' cannot be declared const – David Dec 13 '13 at 19:46
  • 2
    @David just tried in VS 2012. It does.. – nawfal Dec 13 '13 at 19:46
  • 1
    @David Tested in LINQPad. Compiles and runs just fine. – p.s.w.g Dec 13 '13 at 19:47
  • What version of .NET are you two using? IDE/Editor doesn't matter, .NET version does. – evanmcdonnal Dec 13 '13 at 19:50
  • @evanmcdonnal of course editor doesnt matter. When ppl talk about VS version, its the compiler version they are talking about. Tested with .NET 4 and 4.5 using C# 5 compiler. – nawfal Dec 13 '13 at 19:52
  • 3
    Even value type constants can be pretty useless: `const int Two = 2;`. Whether it's useful or not does not depend on syntax rules. – Olivier Jacot-Descombes Dec 13 '13 at 19:55
  • I wouldn't use const for references/classes. I don't really use const that much either. – iefpw Dec 13 '13 at 19:55
  • possible duplicate of [What is the use of declaring a user defined type constant with null value?](http://stackoverflow.com/questions/14946172/what-is-the-use-of-declaring-a-user-defined-type-constant-with-null-value) – nawfal Dec 14 '13 at 05:10
  • 1
    One reason to want to be able to treat `null` as `const` is so that you can provide an optional argument defaulting to `null`. This isn't a justifying feature (default parameters didn't exist in C# 1.0), but it's points in favor of allowing reference types to be declared as `const` (for consistency) now. – Brian Dec 16 '13 at 20:51
  • @Brian spot on. In fact that's the most compelling reason I have seen on this thread. Can you make it an answer? Worth it.. – nawfal Dec 17 '13 at 04:28

7 Answers7

4

Eric Lippert says features are unimplemented by default and every possibility adds more effort in testing, maintaining etc... Why would a null value for a reference type ever needed as a constant?

It was easier to implement the feature that, "any instance field can be marked as const" than to implement the feature that, "any instance field can be marked as const if there are any non-null compile time literals of that type".

You're essentially proposing adding a feature that says, "a field cannot be marked as const if there are no non-null compile time literals of that type." That feature is unimplemented by default and would add more effort in testing, maintaining, etc. if added.

Servy
  • 202,030
  • 26
  • 332
  • 449
4

Servy's point is a good one. Let me explain that point in a slightly different way.

Let's start by asking the more general question "should the C# 1.0 compiler classify a null literal of reference type as a constant?" I want to emphasize that we are reasoning about C# 1.0 here, so any concerns about nullable value types or generics are not on your mind.

Well, what's the point of classifying anything as a constant? The point is that there are certain language constructs that require constants:

  • The value of a case clause
  • Attributes
  • Constant locals and fields

And constants have an effect on reachability analysis:

if (0 == 0) 
  M(out x); // always reached, so x is initialized 
else
  M(out y); // unreachable, so y is not initialized.

Now, let's suppose that we accept that null is useful in attributes and case null and that though it is a bit weird,

if (null == null)

ought to be treated as the constant true. Your proposal then is to say that null is a constant in these three ways, but nevertheless cannot be assigned to a const local or field??? That is a bizarre restriction; if null may be treated like a constant in every other situation where a constant is required, then why should it not be treated as a constant when defining a field or local?

But of course I haven't answered your actual question, which is "when is this useful?"

Well, again, let's push back. When is any constant useful? Constants are treated by the compiler as though their value is substituted into their usage, so why would you ever say:

const int Blah = 0;
...
if (x == Blah)

when you could simply say

if (x == 0) 

? When I put it like that I hope the reasoning is obvious. The reason you use any constant field or local is to give a name to a value so that the reader of the code understands it better. A null constant field or local is exactly the same. It is arguably more clear to read:

if (node != Empty)
    stack.Push(node.Right);

than

if (node != null)
    stack.Push(node.Right);
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • Eric, I think you already answered the question *when is a const really useful* - the four cases you initially mentioned. The latter explanation regarding the readability, well that holds true for any variable, especially `static readonly`, but I get it. – nawfal Dec 15 '13 at 17:16
1

In most cases I'd say just no.
The only case where I can see something like that useful is inside a preprocessor condition. For example:

#if DEBUG
const Object obj = "Debug text";
#else
const Object obj = null;
#endif
MasterMastic
  • 20,711
  • 12
  • 68
  • 90
  • 1
    That's precisely what you *shouldn't* do with `const`. `const` isn't just a compile-time in the same assembly, it's a compile-time constant in all assemblies that reference yours too. If `obj` shouldn't be treated as a compile-time constant across all assemblies, because it might depend on which version of the assembly will be used at runtime, it shouldn't be `const`. (It might still be `readonly`, however.) –  Dec 13 '13 at 20:05
  • @hvd So you're saying null would change its meaning if I load it to another version of the runtime? When would you *ever* use const? I'm not sure I get your point. I'd appreciate a reference to back this up tho so I'll get to the bottom of your words. – MasterMastic Dec 13 '13 at 20:09
  • No, I'm saying if another assembly uses `obj`, and it's built against a debug version of your library, but uses the release version of your library at runtime, it'll still "see" "Debug text". I would only use `const` if I do not expect the value to ever change, and am willing to deal with the breakage if my expectations are wrong. (I don't have a reference handy, but can look it up later, if you like.) –  Dec 13 '13 at 20:13
  • @hvd Oh I understand but little hard time fully agree. I wouldn't expect my debug and release assemblies to ever get mingled, not to mention wouldn't expect my debug builds to get **published**. And in addition I would only partially agree under the assumption this code would be in shared libraries. I *personally* would not avoid this code, but you do raise a valid point, nevertheless, so thank you. – MasterMastic Dec 13 '13 at 20:19
1

It can make sense for (typically recursive) data structures for which null has a special meaning, other than (or in addition to) the usual.

For example, a set implemented where null means the empty set. Implementing a set as a linked list of a binary search tree would make that a natural thing to do. Defining const Set Empty = null would make some sense in that case. Often seen together with abusing operator overloading, and with an abundance of static methods.

This practice matches conventions often used in theoretical Computer Science, it could be seen as theory leaking into practice.

Whether that's a Good Thing(tm) or not is an other matter, but it does happen.

harold
  • 61,398
  • 6
  • 86
  • 164
1

There is pretty common sentinel value pattern where some "special" entity used as "terminate loop" operation. I can see following as reasonable constant for such code (note that clearly sample is very contrived, there are real algorithms that rely on sentinel):

 const Item ItemLookpupSentinel = null; 

 Item Serach(IEnumerable<Item> items)
 {
       var sequenceWithSentinel = 
          items.Concat(Enumerable.Repeat(ItemLookpupSentinel, 1));
       foreach(var item in  sequenceWithSentinel )
       {
             if (item == ItemLookpupSentinel)
                return null;
       }
 }
Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
  • 4
    When I see this is done it is always done with a `static readonly` field that *doesn't* refer to a null reference. – Servy Dec 13 '13 at 20:06
1

An example of a useful reference type constant is:

const string UsefulSite = "http://stackoverflow.com";

Forbidding reference type constants just because assigning null to a constant is not very useful, seems not very appropriate. Value type constants can be pretty useless as well:

const int FourtyTwo = 42;

It's up to you to make the best of the possibilities C# offers to you.

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • That's a good angle to look at it. Btw, of course in the q I meant non-string reference types which I have not worded properly. – nawfal Dec 14 '13 at 04:22
  • Thanks for the answer, yours and Servy's answer answer it perfectly, cant make a distinction. I will accept his answer for being very direct. – nawfal Dec 14 '13 at 04:23
0

From comments, as @Brian points out:

One reason to want to be able to treat null as const is so that you can provide an optional argument (for reference types) defaulting to null. This isn't a justifying feature (default parameters didn't exist in C# 1.0), but it's points in favor of allowing reference types to be declared as const (for consistency) now.

Community
  • 1
  • 1
nawfal
  • 70,104
  • 56
  • 326
  • 368