从字节数组中一般检索不同的数字数据类型

时间:2013-11-22 15:05:25

标签: c# generics

我有一个字节数据数组,但是这个数组可以保存任何无符号数(byte / ushort / uint / ulong)作为字节。

这个问题是再次将数据检索到所需类型:我希望将数据转换回处理程序类中所需的数据类型,而不是再次请求数据的数据(可以转换返回的字节)表示值的数组。)

此外,ref或out变量,虽然是一个解决方案,但是麻烦,因为我分配的成员不一定是我正在返回的类型(阅读显式枚举):

public enum SomeEnum : ulong
{ 
// ...
}

我目前的职能是:

/// <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;
}

这可以合并为一个功能,我将如何做到这一点?像

SomeEnum member = (SomeEnum)GetData<ulong>(dataIndex);

GetData<byte>(0);

public T GetData<T>(byte startingDataIndex) // T would be byte during runtime
{
    return this.Data[dataIndex]; // Compiler: Er, T? Byte? what?
}

/// <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;
}

或者我最好离开时作为单独的功能并保留它们?

3 个答案:

答案 0 :(得分:1)

看一下BitConverter课程。哪个应该能够处理转换为适当的类型或字节数组。如果您需要将它们转换为枚举,则需要使用Enum.ToObject。不幸的是,泛型不是模板,并不是真正针对这种情况设计的。泛型用于处理无关紧要的对象或符合特定接口的情况。对于像这样CLR没有通用描述符的东西,你需要专门化并创建不同的方法。

答案 1 :(得分:1)

可悲的是,您最终必须为要反序列化的每种类型编写函数。这源于你不能在T上真正使用逻辑运算符(&amp;,|等)或数学运算符(=, - ,*等)的事实。此外,对'数字'类型没有约束T所以你不会得到任何帮助。

如果你看一下BinaryReader类,你会看到甚至微软最终编写了一堆不同的函数来从字节流中读取不同的数据类型。

另一方面,你可以写一些基本上路由到正确函数的泛型函数,并以这种方式返回结果。虽然它会更方便,但会有性能损失,但根据您的整体设计方法,这是可以接受的。例如:

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);
  }
}

同样,这不是100%理想,但它会起作用。我使用了类似的方法,我发现在这里和那里添加了几种类型后,在我知道它之前,我支持所有内在类型。

答案 2 :(得分:0)

您需要使用Marshall.Sizeof()而不是sizeof()来计算泛型类型的大小。否则就会直截了当。

    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;
    }

注意| =运算符不能用于泛型类型,因此它需要存储在ulong缓冲区中,我们必须假设它可以容纳足够的字节。