5

I have the following problem. I have an array of bytes that I want to convert intro an array of primitive types. But I don't know the type. (This is given as an array of types). As a result I need an array of objects.

Of course I could use a switch on the types (there are only a limited number of them), but I wonder if there is a better solution for that.

Example:

byte[] byteData = new byte[] {0xa0,0x14,0x72,0xbf,0x72,0x3c,0x21}
Type[] types = new Type[] {typeof(int),typeof(short),typeof(sbyte)};

//some algorithm

object[] primitiveData = {...};
//this array contains an the following elements
//an int converted from 0xa0,0x14,0x72,0xbf
//a short converted from 0x72, 0x3c
//a sbyte converted from 0x21

Is there an algorithm for this or should I use a switch

2
  • Did you look for typed arrays or just object[]? Commented Jul 25, 2010 at 11:51
  • just object[] i want to use the object array to invoke an unknown Constructor with the ConstructorInfo class Commented Jul 26, 2010 at 13:27

5 Answers 5

4

Here's my ideas:

object[] primitiveData = new object[byteData.Lenght];
for (int i = 0; i < bytesData.Lenght; i++)
{
     primitiveData[i] = Converter.ChangeType(bytesData[i], types[i]);
}

object[] primitiveData = new object[bytDate.Lenght];
for (int i = 0; i < bytesDate.Lenght; i++)
{
     Type t = types[i];
     if (t == typeof(int))
     {
          primitiveData[i] = Convert.ToInt32(bytesDate[i]);
     }
     else if (t == typeof(short))
     {
          primitiveData[i] = Convert.ToInt16(bytesDate[i]);
     }
     ..
}

var dic = new Dictionary<Type, Func<byte, object>>
{
    { typeof(int), b => Convert.ToInt32(b) },
    { typeof(short), b => Convert.ToInt16(b) },
    ...
};

byte[] byteData = new byte[] { 0xa0, 0x14, 0x72, 0xbf, 0x72, 0x3c, 0x21 };
Type[] types = new Type[] { typeof(int), typeof(short), typeof(sbyte) };

List<object> list = new List<object>(primitiveData.Length);
for (int i = 0; i < primitiveData.Length; i++)
{
     Byte b = byteData[i];
     Type t = types[i];
     Func<byte, object> func = dic[t];
     list.Add(func(b));
}
object[] primitiveData = list.ToArray();

byte[] byteData = new byte[] { 0xa0, 0x14, 0x72, 0xbf, 0x72, 0x3c, 0x21 };
// delegates to converters instead of just appropriate types
Func<byte, object>[] funcs = new Func<byte, object>[]
{
     b => Convert.ToInt32(b),
     b => Convert.ToInt16(b),
     b => Convert.ToSByte(b)
};

List<object> list = new List<object>(primitiveData.Length);
for (int i = 0; i < primitiveData.Length; i++)
{
     Byte b = byteData[i];
     Func<byte, object> func = funcs[i];
     list.Add(func(b));
}
object[] primitiveData = list.ToArray();

Note, that all my solutions above assumes the symmetry between byteData and types.

Otherwise you have to prepare a symmetric array which will contain an index of asymmetric array:

byte[] byteData = new byte[] { 0xa0, 0x14, 0x72, 0xbf, 0x72, 0x3c, 0x21 };
Type[] types = new Type[] { typeof(int), typeof(short), typeof(sbyte) }; // asymmetric 
int[] indexes = new int[] { 0, 0, 0, 0, 1, 2 }; // symmetric 
Sign up to request clarification or add additional context in comments.

2 Comments

Looks OK but you do need to know how the data was converted to byte[]. There could be byte-order issues.
This solution (I think) does a 1-to-1 mapping on the byte-array. In this case I want a mapping of n bytes to 1 primitive type (where n is the size of the primitve type in bytes (4 for an int, 2 for a short, 8 for a float,...)). Or does the Convert-class uses the array as a pointer?
3

This code uses unsafe to get a pointer to the byte array buffer, but that shouldn't be a problem.

[Edit - changed code after comment]

byte[] byteData = new byte[] { 0xa0, 0x14, 0x72, 0xbf, 0x72, 0x3c, 0x21 };
Type[] types = new Type[] { typeof(int), typeof(short), typeof(sbyte) };

object[] result = new object[types.Length];
unsafe
{
    fixed (byte* p = byteData)
    {
        var localPtr = p;
        for (int i = 0; i < types.Length; i++)
        {
            result[i] = Marshal.PtrToStructure((IntPtr)localPtr, types[i]);
            localPtr += Marshal.SizeOf(types[i]);
        }
    }
}

2 Comments

Seems like a waste to use both a MemoryStream and a pointer, when you could just have a pointer to byteData and do pointer arithmetic.
@Ben: I agree, it was lazy on my part, and I've modified it now.
1

You could use a BinaryReader:

public static IEnumerable<object> ConvertToObjects(byte[] byteData, Type[] types)
{
    using (var stream = new MemoryStream(byteData))
    using (var reader = new BinaryReader(stream))
    {
        foreach (var type in types)
        {
            if (type == typeof(short))
            {
                yield return reader.ReadInt16();
            }
            else if (type == typeof(int))
            {
                yield return reader.ReadInt32();
            }
            else if (type == typeof(sbyte))
            {
                yield return reader.ReadSByte();
            }
            // ... other types
            else
            {
                throw new NotSupportedException(string.Format("{0} is not supported", type));
            }
        }
    }
}

And then:

byte[] byteData = new byte[] { 0xa0, 0x14, 0x72, 0xbf, 0x72, 0x3c, 0x21 };
Type[] types = new Type[] { typeof(int), typeof(short), typeof(sbyte) };
object[] result = ConvertToObjects(byteData, types).ToArray();

Comments

0

Little dirty but it works... sp is used to point to where to read from next in byteData, checking of types can be done some other way I guess... but this is just an idea. So please no -1 me if you dont like it. =)

        byte[] byteData = new byte[] { 0xa0, 0x14, 0x72, 0xbf, 0x72, 0x3c, 0x21 };
        Type[] types = new Type[] {typeof(int),typeof(short),typeof(sbyte)};

        object[] primitiveData = new object[types.Length];
        int sp = 0;

        for(int i=0; i<types.Length; i++)
        {

            string s = types[i].FullName;
            switch(types[i].FullName)
            {
                case "System.Int32":{
                    primitiveData[i] = BitConverter.ToInt32(byteData, sp);
                    sp += 4;
                }break;
                case "System.Int16":
                    {
                    primitiveData[i] = BitConverter.ToInt16(byteData, sp);
                    sp += 2;
                }break;
                case "System.SByte":
                    {
                    primitiveData[i] = (sbyte)byteData[sp];
                    sp += 1;
                }break;

            }
        }

Comments

0

You can use reflection to create the arrays and fill them. (Notice the error handler due to wrong data for SByte):

  [TestMethod]
  public void MyTestMethod() {
     byte[] byteData = new byte[] { 0xa0, 0x14, 0x72, 0xbf, 0x72, 0x3c, 0x21 };
     Type[] types = new Type[] { typeof(int), typeof(short), typeof(sbyte) };

     List<Array> result = new List<Array>();

     foreach (var type in types) {
        Type arrayType = type.MakeArrayType();
        ConstructorInfo ctor = arrayType.GetConstructor(new Type[] { typeof(int) });
        Array array = (Array)ctor.Invoke(new object[] { byteData.Length });

        for (int i = 0; i < byteData.Length; i++) {
           byte b = byteData[i];
           try {
              array.SetValue(Convert.ChangeType(b, type), i);
           } catch {
              Console.WriteLine("Error with type {0} and value {1}", type, b);
           }
        }

        result.Add(array);
     }

     // -------------------
     // show result
     foreach (var array in result) {
        Console.WriteLine(array.GetType());
        foreach (var item in array) {
           Console.WriteLine("   {0}", item);
        }
     }
  }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.