4

I'm trying to get the bytes for different objects of different types using BitConverter.GetBytes. In order to do this, I'd like to write a generic extension method, rather than write individual extension methods for every type I want to work with (short, long, ulong, etc).

Is this possible?

Here's the (not working) code I have so far for the generic method:

public static byte[] Foo<T>(this T input) where T : struct
{
    // error here, no match to overloads
    var result = BitConverter.GetBytes(input);
    // do more logic here...
    return result;
}

By the way, this must be .NET 3.5 compliant.

Jim Fell
  • 13,750
  • 36
  • 127
  • 202
Aaron Thomas
  • 5,054
  • 8
  • 43
  • 89
  • Could you clarify what you mean by "flip the resulting bytes"? Your Array.Reverse will flip the order of the bytes in the array, but not the values within the bytes themselves, which is what I thought you meant. Or did you mean to do a bitwise not of all the bytes? Flipping can mean a few things in this context. – Cody Aug 17 '16 at 20:46
  • I really wish the edit didn't bold "flip". That is not really the issue here, I should have not even included it in the example. Editing to clarify... – Aaron Thomas Aug 17 '16 at 20:47
  • Please clarify what you mean by "not working". – Sam Axe Aug 17 '16 at 20:51
  • I assume by "not working" you mean "not compiling". That's because the compiler doesn't know apriori what type "T" is and there is no "object" overload for `BitConverter.GetBytes()`. You'll need to `switch` on `Type.GetTypeCode(typeof(T))` and cast `input` to the indicated type or use reflection as @Xiaoy312 indicates in his answer. – GreatAndPowerfulOz Aug 17 '16 at 20:54
  • @Great.And.Powerful.Oz you are correct, compile errors. It sounds like even with a `switch` I will have a lot of repeated code here :-( – Aaron Thomas Aug 17 '16 at 20:56

2 Answers2

3

It is not suggested, but you can invoke the BitConverter.GetBytes method dynamically:

public static byte[] ToFlipped<T>(T input) where T : struct
{
    var result = (byte[])typeof(BitConverter).GetMethod("GetBytes", new[] { typeof(T) })
        .Invoke(null, new[] { input });
    Array.Reverse(result);

    return result;
}
Xiaoy312
  • 14,292
  • 1
  • 32
  • 44
  • Your proposed solution looks relatively clean. Why is it *not suggested?* – Jim Fell Feb 04 '19 at 18:26
  • By doing so you risk running into run-time exception, instead of a compile-time error, on type like `YourCustomStruct`. – Xiaoy312 Feb 04 '19 at 18:34
  • On top of that, there is some overhead for using reflection. – Xiaoy312 Feb 04 '19 at 18:36
  • I see, thanks. I think it'll work well for my application, since all of my data is defined as part of the design (nothing supplied by the user at run-time). – Jim Fell Feb 04 '19 at 18:39
2

use GCHandle.Alloc() and pin the struct :-)

public static byte[] Foo<T>(this T input) where T : struct
{
  int size = Marshal.SizeOf(typeof(T));
  var result = new byte[size];
  var gcHandle = GCHandle.Alloc(input, GCHandleType.Pinned);
  Marshal.Copy(gcHandle.AddrOfPinnedObject(), result, 0, size);
  gcHandle.Free();
  return result;
}

... but "Marshal.SizeOf" gives wrong sizes on bool and char.

I have rewritte the SizeOf-function (looks a little crazy, but its extreme fast)

static readonly Dictionary<long, int> SizeOfDict = new Dictionary<long, int>();

//[MethodImpl(MethodImplOptions.AggressiveInlining)] // not supported below 4.5
public static int SizeOf<T>() where T : struct
{
  // --- Highspeed Compiler-Hack ---
  // if (typeof(T) == typeof(byte)) return sizeof(byte); // uncomment if .Net >= 4.5
  // if (typeof(T) == typeof(sbyte)) return sizeof(sbyte);
  // if (typeof(T) == typeof(ushort)) return sizeof(ushort);
  // if (typeof(T) == typeof(short)) return sizeof(short);
  // if (typeof(T) == typeof(uint)) return sizeof(uint);
  // if (typeof(T) == typeof(int)) return sizeof(int);
  // if (typeof(T) == typeof(ulong)) return sizeof(ulong);
  // if (typeof(T) == typeof(long)) return sizeof(long);
  // if (typeof(T) == typeof(float)) return sizeof(float);
  // if (typeof(T) == typeof(double)) return sizeof(double);
  // --- fix wrong sizes ---
  if (typeof(T) == typeof(char)) return sizeof(char);
  if (typeof(T) == typeof(bool)) return sizeof(bool);
  long id = (long)typeof(T).TypeHandle.Value;
  int len;
  if (!SizeOfDict.TryGetValue(id, out len))
  {
    len = Marshal.SizeOf(typeof(T));
    SizeOfDict.Add(id, len);
  }
  return len;
}
MaxKlaxx
  • 713
  • 7
  • 9