Reputation: 3704
I have a data array of bytes, however this array can hold any unsigned number (byte/ushort/uint/ulong) as bytes.
The problem with this is retrieving the data again into the required type: I want the responsibility of converting the data back to the required datatype in the handler class, and not the one requesting the data again (which could convert a returned byte array representing the value).
Furthermore, ref or out variables, though is one solution, are a hassle as members I'm assigning are not necessarily of the type I'm returning (read explicit enums):
public enum SomeEnum : ulong
{
// ...
}
The functions I have at the moment are:
/// <summary>
/// Get data (byte)
/// </summary>
public byte GetDataByte(byte dataIndex)
{
return this.Data[dataIndex];
}
/// <summary>
/// Get data (ushort)
/// </summary>
public ushort GetDataUshort(byte startingDataIndex)
{
ushort output = 0;
for (byte i = startingDataIndex; i < this.Data.Length && i < sizeof(ushort); i++)
{
output |= (ushort)(this.Data[i] << (i * 8));
}
return output;
}
/// <summary>
/// Get data (uint)
/// </summary>
public uint GetDataUint(byte startingDataIndex)
{
uint output = 0;
for (byte i = startingDataIndex; i < this.Data.Length && i < sizeof(uint); i++)
{
output |= ((uint)this.Data[i] << (i * 8));
}
return output;
}
/// <summary>
/// Get data (ulong)
/// </summary>
public ulong GetDataUlong(byte startingDataIndex)
{
ulong output = 0;
for (byte i = startingDataIndex; i < this.Data.Length && i < sizeof(ulong); i++)
{
output |= ((ulong)this.Data[i] << (i * 8));
}
return output;
}
Could this be combined into one function, and how would I go about doing that? Like
SomeEnum member = (SomeEnum)GetData<ulong>(dataIndex);
but
GetData<byte>(0);
public T GetData<T>(byte startingDataIndex) // T would be byte during runtime
{
return this.Data[dataIndex]; // Compiler: Er, T? Byte? what?
}
or
/// <summary>
/// Get data (T)
/// </summary>
public T GetData<T>(byte startingDataIndex)
{
T output = 0; // Compiler: derp, this isn't an integer.
for (byte i = startingDataIndex; i < this.Data.Length && i < System.Runtime.InteropServices.Marshal.SizeOf(typeof(T)); i++)
{
output |= (T)(this.Data[i] << (i * 8)); // Compiler: nor is this!
}
return output;
}
Or am I really better off leaving as separate functions and maintaining them all?
Upvotes: 1
Views: 235
Reputation: 15705
Sadly, you are going to end up having to write a function for each type that you want to deserialize. This stems from the fact that you can't really use logic operators (&,|,etc.) or math operators (=,-,*,etc) on T. Furthermore, there is no constraint for 'numeric' types on T so you'll get no help from that.
If you take a look into the BinaryReader class you will see that even Microsoft ended up writing a bunch of different functions to read the different data types from a stream of bytes.
On the other hand, you could write some kind of generic function that would essentially route to the correct function, and return the results that way. While it would be more convenient there would be a performance penalty, but that can be acceptable depending on your overall design approach. For example:
public T ReadData<T>(int startIndex)
{
Type t = typeof(T);
if (t == typeof(int))
{
return (T)(object)ReadInt(startIndex);
}
else if(t == typeof(byte))
{
return (T)(object)ReadByte(startIndex);
}
else
{
string err = string.Format("Please support the type {0}", t);
throw new NotSupportedException(err);
}
}
Again, this is not 100% ideal, but it will work. I have used a similar approach, and I found that after adding a few types here and there, I had support for all of the intrinsic types before I knew it.
Upvotes: 1
Reputation: 954
You need to use Marshall.Sizeof() instead of sizeof() to be able to calculate the size of a generic type. Otherwise it's straight forward.
public static T GetData<T>(byte startingDataIndex)
{
var length = Marshal.SizeOf(default(T));
ulong buffer = 0;
for (var i = startingDataIndex; i < Data.Length && i < length; i++)
{
buffer |= (Data[i] << (i * 8));
}
return (T)(object)buffer;
}
notice that the |= operator cant be used on the generic type so it needs to be stored in an ulong buffer which we have to assume can hold enough bytes.
Upvotes: 0
Reputation: 7587
Take a look at the BitConverter class. Which should be able to handle the converting to the appropriate type or a byte array. If you need to convert them to an Enum, you'll need to use Enum.ToObject
. Unfortunately, generics are not templates and aren't really designed for this scenario. Generics are for scenario where the object your handling doesn't matter, or conforms to a particular interface. For things like this where the CLR has no general descriptor, you need to specialize and create different methods.
Upvotes: 1