Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't a constant field be of non-built-in struct type in C#?

class T
{
    enum E { }
    struct S { }
    interface I { }
    delegate void D();
    class C { }

    const E e = new E();
    //const S s = default(S); // ERROR
    const I i = default(I);
    const D d = default(D);
    const C c = default(C);

    const int x = 10;
    const string y = "s";

    private void MyMethod(
        E e = new E(),
        S s = default(S), // NO ERROR
        I i = default(I),
        D d = default(D),
        C c = default(C),
        int x = 10,
        string y = "s")
    { }
}

All of the above are possible except for the constant field of struct type.

I can see why the non-string reference types would default to the only literal expression they can represent - null - but if I'm not mistaken the default value of a struct is an instance of the struct with all its fields set to their default value, basically memset(0). Also, for the default arguments of the method MyMethod the compiler has no complaints. So, why is it that a const struct field is not possible?

Is there a reason why or is it just the way C# was written? Oh and by the way, what's the idea of allowing enum types to have a default constructor?

(Question originally found here, where it didn't get a real answer.)

like image 629
johv Avatar asked Nov 24 '14 10:11

johv


People also ask

When should I use struct instead of class?

Class instances each have an identity and are passed by reference, while structs are handled and mutated as values. Basically, if we want all of the changes that are made to a given object to be applied the same instance, then we should use a class — otherwise a struct will most likely be a more appropriate choice.

When should I use a struct instead of a class in C#?

In classes, two variables can contain the reference of the same object and any operation on one variable can affect another variable. In this way, struct should be used only when you are sure that, It logically represents a single value, like primitive types (int, double, etc.). It is immutable.

Can you call new on a struct?

When you create a struct object using the new operator, it gets created and the appropriate constructor is called. Unlike classes, structs can be instantiated without using the new operator. If you do not use new, the fields will remain unassigned and the object cannot be used until all of the fields are initialized.

How do you declare a constant in C sharp?

Constants are declared with the const modifier. Only the C# built-in types (excluding System. Object) may be declared as const . User-defined types, including classes, structs, and arrays, cannot be const .


2 Answers

Fundamentally, true constant fields need to be representable directly in IL as metadata, which means: limiting to a set of known types that work directly in IL. You could say that you can express a "constant" via a constructor call and substitute that constructor call whenever the constant is used, etc, but:

  1. that would be simulating something very close to static readonly, which already exists and can be used
  2. it would not be provably constant - since your custom type could do anything internally

Basically, just use static readonly in your case:

static readonly S s = default(S);

In the case of enums: all value types both have a default constructor (in C# terms) and don't have a default constructor (in IL terms). Schrödinger's constructor! All the semi-existent default constructor means is "initialize this to zero", and it works for any value type (it is the initobj IL instruction). Actually, in C# you can also do this in two other ways for enums: default(TheEnum), and 0 - since the literal 0 works for any enum.

like image 166
Marc Gravell Avatar answered Nov 15 '22 03:11

Marc Gravell


The designers of C# seem to take the viewpoint that the language should forbid constructs which would have semantics that were well-defined but didn't seem to be useful, especially if such constructs would represent things the designers didn't think people should be doing.

For example, C# forbids the use of sealed types as generic constraints. If class Foo isn't sealed, then a declaration class Bar<T> where T:Foo will declare a generic class family Bar<> for which the T would have to be Foo or something derived from it. Semantically, there's no reason that construct couldn't have the exact same meaning even if Foo were a sealed type; to be sure, unless Foo were to change, there wouldn't be any way T could be anything other than Foo, and the designers of C# would probably say that the code should simply use Foo rather than T [though if Foo were ever unsealed, the meaning of the generic code would no longer match the code that specified Foo].

Likewise, I suspect that while there wouldn't have been any particular problem with C# allowing a value type to declare a constant equal to the the default value of that type, the normal purpose of constants is to provide a single patch point for things that might need to change. Any code which declared const MyPoint = Default(Point); to declare a default location of (0,0) may have been up a creek if it became necessary to have a different default value.

On the other hand, the fact that C# doesn't allow the declaration of non-default-valued constants of non-primitive value types other than Decimal doesn't mean that C# wouldn't be cable of passing through the values of constants declared in other assemblies if the language designers had chosen to allow that. If C# had allowed value-type constants declared in other assemblies to be used as value-type constants in C#, then if it became necessary to use some value other than the default for a Point constant, it would be possible to add to the assembly a reference to a CIL module which defined a Point constant containing the sequence of byte which would represent the necessary coordinates, and then have the C# module use the constant from the CIL assembly.

I disagree with the idea that static readonly should be considered a 100% adequate substitute for const, since values of constants may be used in contexts where values of static readonly variables cannot. Still, the fact that C# is unwilling to consider value-type constants from other modules to be legitimate constant expressions unless the types in question are ones it knows about would greatly limit the usefulness of being able to define constants of arbitrary value types.

Addendum

Discussions elsewhere have brought up a more general problem with value-type constants: they're stored as a sequence of bytes, which means that their meaning will be very dependent upon implementation details of the type in question; if later versions of a type store things differently, there would be no mechanism to prevent the byte sequence from being totally misinterpreted. As a simple example, if one version of a type Point had int fields X and Y in that order, but later version changed the sequence to Y, X, then a Point constant which was compiled with X=1 Y=2 would be interpreted as having Y=1 X=2.

like image 37
supercat Avatar answered Nov 15 '22 04:11

supercat