51

They are immutable value types on the stack. What keeps me from having them a const?

References:

Lazlo
  • 8,518
  • 14
  • 77
  • 116

4 Answers4

64

Because the value type constructor might do anything -- for example, switch logic based on the time of day. Constant value types makes sense intellectually, but it simply cannot work on custom value types in practice due to the flexibility of constructors to do whatever they please. (Remember that constants are evaluated at compile time, which means your constructor would have to be run at compile time.)

AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
cdhowie
  • 158,093
  • 24
  • 286
  • 300
  • 1
    Thanks, will accept when S/O lets me. Too bad, I have a "legit" constructor if I may say, and I'd love having it const. But oh well, I'll find another design I guess. – Lazlo Jan 04 '11 at 05:13
  • 2
    @Lazlo: Even still, the compiler would have to call it while it's compiling. But the compiler isn't technically supposed to run your code. – cdhowie Jan 04 '11 at 05:15
  • 3
    I guess I'm looking for the equivalent of C++'s aggregate or POD structs: http://en.wikipedia.org/wiki/C%2B%2B_classes Something that is just a different value type or group of data. Oh well. – Lazlo Jan 04 '11 at 05:18
  • 1
    Structs can be created without a constructor. Code which has access to a struct's fields can simply populate them with the desired value; a compiler which had a list of fields and values could create a struct instance which was suitably configured. – supercat Jan 27 '13 at 04:33
  • @supercat Technically, all structs have a no-arg constructor, but of course that one would be suitable for construction of const values. While using the no-arg constructor with object initialization syntax using only constant values could work, the language designers have not chosen to implement this feature. I cannot imagine the performance benefit from initializing fields at compile-time is so great that it warrants an addition to the language. – cdhowie Jan 28 '13 at 15:24
  • 3
    @cdhowie: The issue would not be one of performance so much as semantics, as `const` values can be used in cases where static read-only fields cannot (e.g. as default parameter values). They are also "baked into" compiled code, which in some cases is a bad thing but in other cases may be useful. – supercat Jan 28 '13 at 16:29
  • But why is it possible in C++/CLI then? E.g. with `value class CLocation`, which is a C# `struct`, I can set `const CLocation loc1 = CLocation(1,2,3)`. Or is the CLI just cheating and not actually using the const? However, it behaves like a const value type. – Tobias Knauss Sep 18 '16 at 17:44
  • @TobiasKnauss C# and C++/CLI are different languages. The C++ concept of `const` is closer to the C# concept of `readonly`, while the C++ concept of `constexpr` is closer to the C# concept of `const`. They aren't exactly identical though -- each language concept has its own nuances. Note also that C++/CLI assemblies are mixed-mode, and can contain native code in addition to IL. The native code sections don't have to play by the CLR's rules. (I'd be interested in a specific code example using a const value class, as well as the compiler output.) – cdhowie Sep 18 '16 at 18:58
  • @cdhowie: Thanks. I have already asked another question about that at SO, linked with this one, so you'll find it in the links at the right side. But here's the shortcut: http://stackoverflow.com/q/39560963/2505186 – Tobias Knauss Sep 18 '16 at 19:01
27

Const in C# means it can be determined at compile time, which is why only very primitive types such as int and string can be a const.

If you are coming from a C background, the readonly keyword might better suit you.

vcsjones
  • 138,677
  • 31
  • 291
  • 286
  • 3
    This is the real answer for programmers with C/C++ background. – eonil Jul 31 '13 at 03:42
  • 1
    @Eonil Not 100%; the C/C++ `const` keyword serves the purposes of both C#'s `const` and `readonly` -- for example, adding two `readonly int` fields in C# will not result in constant folding, while adding two `const int` variables in C/C++ will (if the values are known at compile-time). C#'s `const` is closer to the C++11 `constexpr` modifier, while C#'s `readonly` doesn't have a single analog in C/C++. – cdhowie Sep 22 '14 at 23:48
  • @cdhowie, I think the analogue to `readonly T` `where T : struct` would be `const T&` (in C++). However, most of the time one comes to the `readonly` keyword in C#, one really is looking for C++’s support for “const correctness” which doesn’t exist in C# (and the new set of Immutable classes/pattern tries to provide a similar but quite clumsy and labor-intensive alternative). – binki Mar 11 '15 at 18:37
  • @binki Disagree. A const reference is an alias for another object that exists somewhere else. `readonly T` in C# creates an immutable field, where the value is stored in that field itself -- not elsewhere, as would be the case with a C++ reference. – cdhowie Mar 15 '15 at 06:44
  • 3
    "...which is why only very primitive types such as int and string can be a const." I disagree, and so does C++ (since C++11). I don't see why we can't have something like C++ constexpr in C#. – Tom Lint Dec 22 '15 at 12:52
4

For the C# compiler to produce a const value of a structure type it must know what values should go in all its fields. The C# compiler intrinsically knows how to initialize the fields of certain types like Decimal, but for most value types it has no such knowledge.

It would be possible for a compiler to provide a means of declaring constant values of a structure type in contexts where all of the struct fields were exposed. If a structure's fields were private, constants of that type could then be declared only within the structure; if the fields were internal, constants could be declared anywhere within the assembly; if public, they could be declared anywhere.

Although I would like to see such a feature, I do not expect any mainstream .net languages to implement it. Named constants of types the compiler inherently knows about can participate in other constant expressions, whereas static readonly variables cannot. If NumRows is a constant equal to 4, an expression like Arr[3*NumRows+7] can be replaced by Arr[19] even if NumRows is defined in an outside assembly. This gives such constants a substantial advantage over static readonly variables. If, however, a constant is of a type that a compiler does not intrinsically recognize, it would have very limited ability to participate in any constant expressions, effectively negating the advantage of its being a constant in the first place. If a value-type constant had an exposed field, it would be possible for a compiler to use the value of that field as a constant, but since the creators of .net languages are philosophically opposed to structures with exposed fields, I would not expect them to allow such usage even if they could.

There would be some potential use cases for favoring constants over static readonly variables, but many such cases can be handled acceptably using existing types. For example, a library might expose a const Int64 to encode information about its version, and use that value as a default parameter value for a GetLinkedVersionInfo method. The value in question would get "baked into" the calling code when it was compiled, thus allowing the method to report what version of the library the caller was linked with, and possibly identify whether there are any compatibility problems with the version it's running.

binki
  • 7,754
  • 5
  • 64
  • 110
supercat
  • 77,689
  • 9
  • 166
  • 211
3

I just tested the readonly keyword with a simple mutable struct:

struct Test
{
   public int value;

   public void setInt(int val)
   {
      value = val;
   }
}

static class Program
{
   public static readonly Test t = new Test();

   static void Main()
   {
      Console.WriteLine(t.value); // Outputs "0"
      t.setInt(10);
      //t.value = 10;  //Illegal, will not let you assign field of a static struct
      Console.WriteLine(t.value); // Still outputs "0"
   }
}

Even though a readonly struct isn't technicly a compile time constant, the runtime wont let it change. Even stepping threw the setInt() method it looks like value changes, but does not show the change in Main.

I guess the struct itself is placed in "readonly" memory, disallowing them to change. Unlike a class, which just keeps the pointer constant, allowing the class fields themselves to change as they wish.

So it appears static readonly is effectivly a const, even for mutable structs.

Cemafor
  • 1,633
  • 12
  • 27
  • 4
    All instance methods, properties, and events on a struct receive a reference to the struct on which they should operate--equivalent to receiving `this` as a `ref` parameter--but for an annoying quirk in vb.net and C#: if a struct method or property is invoked on a read-only struct instance, the compiler will silently make a copy of that instance and pass that to the code in question. This yields slow but correct semantics in cases where the code does not modify the structure, and broken semantics in cases where the code does modify the structure. I wish Microsoft would define an attribute... – supercat Jan 24 '13 at 22:48
  • 3
    ...that would allow struct properties and methods to indicate whether they affect the passed-in method, so that the compiler could allow methods that don't modify `this` to be used on read-only structs but forbid the use of methods that do. Otherwise, your best bet is to put up with the annoying and ugly syntax `public static void setInt(ref Test it, int val) {it.value = val;}` and `Test.setInt(ref t, 10);` which would correctly squawk if you tried it on a read-only variable `t`. – supercat Jan 24 '13 at 22:51
  • @supercat, so we really just want MS to finish copying the semantics of C’s `const` into C# :-D – binki Mar 11 '15 at 18:40
  • @binki: Const-correctness in C++ has a reputation for causing a lot of headaches when pointers which could be declared `const`, aren't. Perhaps in part because of that, Java avoided any concept of "read-only reference" types. What detractors of const-correctness fail to recognize, however, is that when writing correct code it's necessary to know whether a method may modify a passed-in object, assume that passed-in objects will never be modified by anything else, or neither; that the language provides no means of expressing such things doesn't mean the programmer doesn't need to know them... – supercat Mar 11 '15 at 21:25
  • ...or that incorrect beliefs regarding what methods may do with or expect from passed-in objects won't result in bugs. With regard to `ref` parameters versus object references, however, I think .NET would have been much better with distinct copy-in, copy-out, copy-in-out, read-only-ref, and read-write-ref parameter types. – supercat Mar 11 '15 at 21:28
  • @supercat Since C# already supports `readonly` value members, it would seem very natural to extend `ref` like `void DoThing(readonly ref MyStruct s);` as `ref` parameters already otherwise behave like members. Add language and runtime support for “pure” (or just use the `readonly` keyword again?) functions, and you’d have the important pieces of const correctness semantics. If only… – binki Mar 11 '15 at 21:59