-2

I have this struct in C#, I wanted to convert the struct to a byte[] so I thought I’d use Marshal for that.

The first thing I did was to get the size in memory of the struct, given that it has 2 int and a long I expected Marshal.SizeOf(myStruct); to return 16 but instead it returned 4.

    public struct ConnectionRequest
    {
        public const long protocolId = 0x41727101980;
        public const int action = 0;
        public int transactionId;
    }

This struct, however, returned the expected size of 16, which makes me think that the const members in the first struct aren’t really IN the struct.

    public struct ConnectionRequest
    {
        public long protocolId;
        public int action;
        public int transactionId;
    }

I assume that the dotnet compiler must be doing some clever magic with the const members but how can they not be in the struct?

Do you know why this happens?

Thanks

Santanor
  • 566
  • 1
  • 7
  • 20
  • A `const` member of a type simply provides a symbolic representation of the literal associated with the const. It's like a static member of type type (except even more static). Every instance of that type has acces to the value the const represents. Each instance doesn't carry around its own copy. – Flydog57 Jan 25 '21 at 01:08
  • [This probably best answers your question](https://stackoverflow.com/a/23529863/3181933) – ProgrammingLlama Jan 25 '21 at 01:16

3 Answers3

1

const is a compile time construct that is guaranteed by the CLI specs to be compiled into the assembly (or for dynamic code into memory) for the Virtual Execution System (VES) to use directly as a literal.

The authoritative source can found in the ECMA-335 Common Language Infrastructure (CLI)

I.8.6.1.2 Location signatures

...

  • The literal constraint promises that the value of the location is actually a fixed valueof a built-in type. The value is specified as part of the constraint. Compilers are required to replace all references to the location with its value, and the VES therefore need not allocate space for the location.

...

However, let's not take their word for it.

Unmanaged size

public struct Test1
{
   public const long protocolId = 0x41727101980;
   public const int action = 0;
   public int transactionId;
}
public struct Test2
{
   public int transactionId;
}

Usage

 Console.WriteLine(Marshal.SizeOf(new Test1()));
 Console.WriteLine(Marshal.SizeOf(new Test2()));

Output

4
4

Emitted CIL

Sharp IO

public struct Test1
{
  public const long protocolId = 0x41727101980;
  public const int action = 0;
  public int transactionId;
}
public struct Test2
{
  public int transactionId;
}
public void M() 
{     
    Console.WriteLine(Test1.protocolId);
}

Output

public void M()
{
    Console.WriteLine(4497486125440L);
}

CIL

IL_0000: ldc.i8 4497486125440
IL_0009: call void [System.Console]System.Console::WriteLine(int64)
IL_000e: ret
halfer
  • 19,824
  • 17
  • 99
  • 186
TheGeneral
  • 79,002
  • 9
  • 103
  • 141
1

You appear to have a fundamental misunderstanding of what const in C# does.

It does not do what it does in C/C++, that would be instead be similar to a readonly field.

const is just that, a compile-time constant that the C# compiler directly inlines anywhere it is used. It does not relate to the instance at all.

To quote the spec:

Even though constants are considered static members, a constant_declaration neither requires nor allows a static modifier.

As described in Constant expressions, a constant_expression is an expression that can be fully evaluated at compile-time.

So they are not part of the class or struct instance at all.

Charlieface
  • 52,284
  • 6
  • 19
  • 43
0

const fields are implicitely static, i.e. they do not contribute to the instance size (but are allocated only once for the type).

This is how your struct compiles to IL: .

class public sequential ansi sealed beforefieldinit ConnectionRequest
    extends [System.Runtime]System.ValueType
{
    // Fields
    .field public static literal int64 protocolId = int64(4497486125440)
    .field public static literal int32 action = int32(0)
    .field public int32 transactionId

}
Klaus Gütter
  • 11,151
  • 6
  • 31
  • 36