Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Explicit struct in C# requires double init of value

Tags:

c#

The following C# struct is used to represent a union of color components and the 32bit color value itself. The problem is that the compiler gives the error:

Error CS0171 Field 'Color.ARGB' must be fully assigned before control is returned to the caller

Is it possible to get rid of this error without initialize the data twice? Is this expected behavior of C#? If I init twice, will the JIT detect the dual init and only do the second one?

[StructLayout(LayoutKind.Explicit)]
public struct Color
{
    public Color(byte r, byte g, byte b, byte a = 0xff)
    {
        ARGB = 0; // The init I shouldn't have to do
        A = a;
        R = r;
        G = g;
        B = b;
    }

    [FieldOffset(0)]
    public byte B;
    [FieldOffset(1)]
    public byte G;
    [FieldOffset(2)]
    public byte R;
    [FieldOffset(3)]
    public byte A;
    [FieldOffset(0)]
    public uint ARGB;

    public static readonly Color Red = new Color(255, 0, 0);
    public static readonly Color Green = new Color(0, 255, 0);
    public static readonly Color Blue = new Color(0, 0, 255);
}
like image 628
maloo Avatar asked Dec 10 '22 03:12

maloo


2 Answers

Is it possible to get rid of this error without initialize the data twice?

Yes and no.

The supposition here is that the members are not already "initialized twice". When you get the new struct from the memory allocator -- either from the heap or the stack -- it will be automatically zeroed out.

As Naidu's answer notes, calling the default constructor indicates to the compiler "the runtime must zero this thing out if it is not already; I wish to assert that I am fine with any portion of the object not written to by the constructor being left in its default state ".

In practice, typically the jitter has already initialized to zero, so typically there is no extra initialization done. However, the behaviour that memory allocators automatically initialize state to zero is runtime-implementation-dependent. Similarly it is an implementation-dependent behaviour whether or not the jitter can optimize away the zero-out behaviour if it knows that every field is initialized.

There are subtleties here. Suppose for example the memory is not zeroed out because the jitter has deduced that your constructor writes every field. Now suppose a thread abort exception is thrown halfway through the constructor. Is it possible for another thread to observe the not-zeroed-out, not-written-by-you state of the object? What hellish behaviour might that wreak, if in fact it is possible? Give that some thought.

Is this expected behavior of C#?

Yes.

The compiler has no idea whatsoever that you're creating a type-unsafe union. It doesn't know the meanings of those attributes.

If I init twice, will the JIT detect the dual init and only do the second one?

There are many different jitters on many different platforms. If you want an answer to your question, try it on all of them with all possible configurations and see what happens.

Regardless, you are likely worrying about nothing important. Writing zeros into memory is pretty fast. Doing an unnecessary zero write is probably not the bottleneck in your program.

like image 133
Eric Lippert Avatar answered Dec 30 '22 17:12

Eric Lippert


Look into below Microsoft link:

https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/cs0843

It says,

To assign a value to an automatically-implemented property from a constructor, you must first invoke the default constructor to create the object.

Doing below change will resolve your issue. call default constructor.

 public Color(byte r, byte g, byte b, byte a = 0xff):this()
 {
         A = a;
         R = r;
         G = g;
         B = b;
  }
like image 25
Pavan Chandaka Avatar answered Dec 30 '22 15:12

Pavan Chandaka