Why doesn't a constant field have an inline structure 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 is possible, with the exception of the const field of type struct .

I see why non- string reference types by default will have the only literal expression that they can represent - null , but if I'm not mistaken, the default value for struct is an instance of the structure with all its fields set by default, basically memset(0) In addition, for the default arguments of the MyMethod method, the compiler has no complaints. So why is the const struct field impossible?

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

(The original question was found where he did not get a real answer.)

+5
source share
2 answers

In fact, true constant fields must be represented directly in IL as metadata, which means: restricting the set of known types that work directly in IL. We can say that you can express a “constant” through a constructor call and replace the constructor call whenever a constant is used, etc., But:

  • which will simulate something very close to static readonly , which already exists and can be used
  • this would not be necessarily permanent - since your custom type could do something internally

Basically, just use static readonly in your case:

 static readonly S s = default(S); 

In the case of enumerations: all value types have a default constructor (in C # terms) and do not have a default constructor (in IL terms). Schrödinger constructor! All semi-equivalent standard constructors mean "initialize this to zero", and it works for any type of value (this is the initobj IL initobj ). In fact, 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.

+5
source

C # designers seem to believe that language should prohibit constructs that would have a certain semantics, but would not seem useful, especially if such constructs reflected things that designers did not think people should do.

For example, C # prohibits the use of private types as general restrictions. If the Foo class is not sealed, then declaring the class Bar<T> where T:Foo will declare the Bar<> class family, for which T must be Foo or something derived from it. Semantically, there is no reason that the construct could not have an exact value, even if Foo was a sealed type; if Foo hadn't changed, there would have been no way for T be anything other than Foo , and C # developers would probably say that the code should just use Foo , not T [although if Foo had ever been opened, the value of the common code would no longer correspond to the code specified by Foo ].

Similarly, I suspect that although there would be no particular problem with C # allowing a value type to declare a constant equal to the default value of that type, the usual purpose of constants is to provide a single patch point for things that may need to be changed. Any code that declared const MyPoint = Default(Point); to declare a default location (0,0), may have been driving if it became necessary to have a different default value.

On the other hand, the fact that C # does not allow the declaration of non-standard constants of non-primitive value types other than Decimal does not mean that C # will not be a cable for passing constant values ​​declared in other assemblies if the language developers decided to allow this. If C # allowed the 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 value for the Point constant, it would be possible to add to assembly is a reference to the CIL module that defined the Point constant containing a sequence of bytes that will represent the necessary coordinates, and then use the C # module with a constant from the CIL assembly.

I disagree with the idea that static readonly should be considered a 100% adequate substitute for const , since constant values ​​can be used in contexts where readonly static variable values ​​cannot. However, the fact that C # does not want to consider constants of the type of values ​​from other modules as legitimate constant expressions, unless these types are the ones that it knows about, would greatly limit the usefulness of determining constants of arbitrary types of values.

Adding

Discussions elsewhere caused a more general problem with the values ​​of constant constants: they are stored as a sequence of bytes, which means that their value will greatly depend on the implementation details of the type in question; if later versions of the type store things differently, there would be no mechanism to completely eliminate the sequence of bytes. As a simple example, if one version of the Point type had int fields X and Y in that order, but a later version changed the sequence to Y , X , then a Point constant that was compiled with X = 1 Y = 2 will be interpreted as having Y = 1 X = 2.

+2
source

Source: https://habr.com/ru/post/1207569/


All Articles