[Last Call] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 6269
  • Last Modified:

Implementing a Union in C#

Hi All,
I'm trying to implement a C style union in C# by overlaying an array of 8 Bytes over the top of an array of 2 uint's using the FieldOffset attribute.

If you have a look at my code below, you'll get the idea for what I'm trying to do, but it doesn't work the way I want. I'm not even sure if this can be done in C#

[StructLayout(LayoutKind.Explicit)]
public struct TestUnion
{
    [FieldOffset(0)]  public uint[] ArrayUint;
    [FieldOffset(0)] public Byte[] ArrayByte;
}

TestUnion MyUnion;
MyUnion.ArrayByte = new Byte[8];
MyUnion.ArrayUint = new uint[] { 0x11223344,0x55667788 };

When I load the uint array, as above, I want the Byte array to contain
[0x11, 0x22, 0x33, 0x44, 0x55,0x66,0x77,0x88]

Like I said, I don't know if this is possible in C#, but if anyone has any ideas I'd love to head them.  -Cheers
0
GavinS
Asked:
GavinS
  • 4
  • 4
1 Solution
 
Jaime OlivaresCommented:
try with something like this:

[StructLayout(LayoutKind.Explicit)]
public struct TestUnion
{
    [FieldOffset(0)]  
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=2)]
    public uint[] ArrayUint;

    [FieldOffset(0)]
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=8)]
    public Byte[] ArrayByte;
}

public byte[] overlay;
0
 
discon0Commented:
The problem is that in C# arrays are reference types so it's their reference that gets to be in the union and not their actual data.

The code above will pass the compiler check but will throw a runtime exception when you try to access ArrayByte[2] - ArrayByte[7].

You could do it using unsafe code, by getting the address of the the first element of the uint array and casting it to a pointer to bytes, or you could use something like this:

      [StructLayout(LayoutKind.Explicit)]
      public struct TestUnion
      {
            [FieldOffset(0)]
            public uint Uint1;
            [FieldOffset(4)]
            public uint Uint2;

            [FieldOffset(0)]
            public byte Byte1;
            [FieldOffset(1)]
            public byte Byte2;
            [FieldOffset(2)]
            public byte Byte3;
            [FieldOffset(3)]
            public byte Byte4;
            [FieldOffset(4)]
            public byte Byte5;
            [FieldOffset(5)]
            public byte Byte6;
            [FieldOffset(6)]
            public byte Byte7;
            [FieldOffset(7)]
            public byte Byte8;
      }

                  TestUnion tu = new TestUnion();
                  tu.Uint1 = 0x11223344;
                  tu.Uint2 = 0x55667788;
                  MessageBox.Show(tu.Byte3.ToString("X"));
0
 
GavinSAuthor Commented:
Hi jaime_olivares:
Well I tried implementing your struct layout, but when I initialised the uint array as below, the byte array just ends up looking like the uint array with 2 32bit elements instead of 8 8bit elements

private void TestTheUnion()
{
    testUnion MyUnion;
    MyUnion.ArrayUint = new uint[] { 0x11223344, 0x55667788 }

}
0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
GavinSAuthor Commented:
Hi discon0:
This is exactly how I've layed out my struct at the moment. Although it works I have to address each member of the struct one line at a time and it ends up looking a bit long winded. There must be a more efficient way to do this.

Alternately, would it be possible to layout the struct as you have and use a loop to iterate through the members of the struct. I'm not sure how you could loop through members of a struct
0
 
discon0Commented:
Unfortunately there is no "perfect" way to do this in C#.

You could try to iterate the members of the struct using reflection but that would be very inefficient. You could try unsafe code.

Alternately, you could forget the union and use two arrays, one for uints and one for bytes and use Buffer.BlockCopy.

Example:

                  byte[] b = new byte[8];
                  uint[] u = new uint[2];
                  u[0] = 0x11223344;
                  u[1] = 0x55667788;
                  Buffer.BlockCopy(u, 0, b, 0, 8); // copy 8 bytes from u, index 0 to b, index 0

0
 
discon0Commented:
I forgot to add another option you have, use a class with a big "getter/setter" method that would work similar to the following:

      class Test
      {
            private byte[] ArrayByte = new byte[8];

            public uint[] ArrayUint
            {
                  get
                  {
                        uint u1 =
                              ((uint)ArrayByte[3]) << 24 |
                              ((uint)ArrayByte[2]) << 16 |
                              ((uint)ArrayByte[1]) << 8 |
                              ((uint)ArrayByte[0]);
                        uint u2 =
                              ((uint)ArrayByte[7]) << 24 |
                              ((uint)ArrayByte[6]) << 16 |
                              ((uint)ArrayByte[5]) << 8 |
                              ((uint)ArrayByte[4]);
                        return (new uint[] { u1, u2 });
                  }
                  set
                  {
                        ArrayByte[0] = (byte)(value[0] & 0xFF);
                        ArrayByte[1] = (byte)((value[0] >> 8) & 0xFF);
                        ArrayByte[2] = (byte)((value[0] >> 16) & 0xFF);
                        ArrayByte[3] = (byte)((value[0] >> 24) & 0xFF);

                        ArrayByte[4] = (byte)(value[1] & 0xFF);
                        ArrayByte[5] = (byte)((value[1] >> 8) & 0xFF);
                        ArrayByte[6] = (byte)((value[1] >> 16) & 0xFF);
                        ArrayByte[7] = (byte)((value[1] >> 24) & 0xFF);
                  }
            }
      }

See also the BitConverter class which you can use to easily convert a uint to a byte[] and vice versa.
0
 
GavinSAuthor Commented:
Hi discon0:
The Buffer.BlockCopy command Kind 0f, Sort of....almost works :-)   The elements of the byte array are present, but out of order. (ie 0x44, 0x33, 0x22, 0x11, 0x88, 0x77, 0x66, 0x55)
0
 
discon0Commented:
This is the correct order. :)

When you have an integer like 0x11223344 on x86 systems (little-endian), 0x11 is the high-order byte, or the 4th byte and 0x44 is the low-order byte, or the 1st.

That's how the number is represented in binary in memory.

See http://en.wikipedia.org/wiki/Endianess
0
 
GavinSAuthor Commented:
Hi discon0:
I'm going with the BlockCopy solution. This actually works quite nicely. I just need to modify my own code slightly and the problem is fixed.   -Thanks
0

Featured Post

New feature and membership benefit!

New feature! Upgrade and increase expert visibility of your issues with Priority Questions.

  • 4
  • 4
Tackle projects and never again get stuck behind a technical roadblock.
Join Now