I'm currently facing a problem, where I'd like to merge unaligned bytes of each property of a class and therefore basically get a serialized byte array only representation (depth-first serialization). By unaligned, I mean the case, where not a full byte/int/short is needed, but only just a few bits of each property.
So, consider this entity class:
public class Data
{
public byte Type { get; set; }
[BitfieldLength(4)] public byte NumberOfOptions1 { get; set; } // nibble
[BitfieldLength(4)] public byte NumberOfOptions2 { get; set; } // nibble
public uint MajorVersion { get; set; }
public byte MinorVersion { get; set; }
[BitfieldLength(24)] public uint TTL { get; set; } // 3 bytes
public ushort EventsCounter {get; set;}
// 12 bytes total
}
The BitfieldLength is a custom attribute class:
[AttributeUsage(AttributeTargets.Property)]
public class BitfieldLengthAttribute : Attribute
{
public BitfieldLengthAttribute(uint length)
{
this.Length = length;
}
public uint Length { get; }
}
Here is the extension code to extract the bytes from each property:
public static class Extensions
{
public static byte[] GetBytesFromProperty(this PropertyInfo field, Data data)
{
Type propertyType = field.PropertyType;
if (propertyType == typeof(ushort))
{
return BitConverter.GetBytes((ushort) field.GetValue(data));
}
else if (propertyType == typeof(uint))
{
return BitConverter.GetBytes((uint) field.GetValue(data));
}
else if (propertyType == typeof(byte))
{
return new[] {(byte) field.GetValue(data)};
}
else
{
throw new Exception($"Unknown type: {propertyType}");
}
}
}
and finally the last part:
var data = new Data();
data.Type = 1;
data.NumberOfOptions1 = 2;
data.NumberOfOptions2 = 3;
data.MajorVersion = 5;
data.MinorVersion = 1;
data.TTL = 20;
data.EventsCounter = 120;
foreach (var prop in typeof(Data).GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
byte[] bytes = prop.GetBytesFromProperty(data);
Console.WriteLine("Name: {0} => Value: {1} => Type: {2} => Bytes: {3}", prop.Name,
prop.GetValue(data), prop.PropertyType,
BitConverter.ToString(bytes));
}
If I'd concatenate each byte array from each property, the output would be 14 bytes instead of the expected 12 bytes...
And here is, where I'm stuck. How to twiddle this bits and bytes to the correct output?
EDIT:
Someone in the comments noted that the order of the properties is unpredictable. That's true, however, please take a look to this solution: https://stackoverflow.com/a/17998371/1362848.