-1

(The question title has been updated to reflect that the struct must eventually be converted using the Marshal class.)

I have some structs that by design will always contain fixed values. For example, let's assume a struct that contains two bytes:

public struct ExampleStruct {
    public byte b0;
    public byte b1;
}

How can I update that implementation so that b0 always holds the value 0 and b1 always holds the value 1? I've tried various combinations of const, readonly, and constructors, all with no luck.

Ideally I'll be able to use it like this and have the bytes initialized to the correct values:

ExampleStruct es = new ExampleStruct();

Note that I eventually need to convert the struct to a byte[] using the Marshal class so it can be sent to a driver. I have verified that this prohibits the use of properties within the struct, as those properties are ignored during the Marshal to byte[]. Also note that in this case Marshal != Serialize, as I need the actual struct byte contents and not a serialized string that can be deserialized later.

Andrew Cottrell
  • 3,312
  • 3
  • 26
  • 41
  • 2
    For a struct, it may be zero-initialized, and that's unavoidable. You can use properties though, and avoid fields altogether: `public byte b0 => 0; public byte b1 => 3;` – canton7 Jul 29 '20 at 16:40
  • For serialization properties usually work better than fields, so maybe properties *are* the solution – Hans Kesting Jul 29 '20 at 18:05

1 Answers1

2

The problem with structs is that you cannot ensure that they will be initialized. If a struct is a class field, then it will be nulled when the containing object is created. It is then perfectly legal to access its fields and properties without first calling new.

You can use getter-only (i.e., read-only) properties instead:

public struct ExampleStruct
{
    public byte B0 => 0;
    public byte B1 => 1;
}

This is equivalent to:

public struct ExampleStruct
{
    public byte B0 { get { return 0; } }
    public byte B1 { get { return 1; } }
}

Another option is to declare constants:

public struct ExampleStruct
{
    public const byte B0 = 0, B1 = 1;
}

however, here you must access the constants through the type name, since constants are static.

var x = ExampleStruct.B1;

Answer to updated question:

You can use a static factory method or property to initialize the struct:

public struct ExampleStruct
{
    private byte _b0;
    private byte _b1;

    public byte B0 { get => _b0;  }
    public byte B1 { get => _b1; }

    public static ExampleStruct ZeroOne => new ExampleStruct { _b0 = 0, _b1 = 1 };
}

You can create a value with:

ExampleStruct es = ExampleStruct.ZeroOne;

// Test whether it can by marshalled to byte[]
byte[] bytes = GetBytes(es);
Console.WriteLine(bytes[0]); // --> 0
Console.WriteLine(bytes[1]); // --> 1

Method used for this test:

// Adapted from: https://stackoverflow.com/a/3278956/880990, Vincent McNabb
static byte[] GetBytes(ExampleStruct es)
{
    int size = Marshal.SizeOf(es);
    byte[] arr = new byte[size];

    IntPtr ptr = Marshal.AllocHGlobal(size);
    Marshal.StructureToPtr(es, ptr, true);
    Marshal.Copy(ptr, arr, 0, size);
    Marshal.FreeHGlobal(ptr);
    return arr;
}

This works. Note that the properties are only used to access the private fields, but it's the fields that are copied with the Marshal class.

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188