【问题标题】:Generically retrieving different numeric data types from byte array通常从字节数组中检索不同的数值数据类型
【发布时间】:2013-12-07 12:28:50
【问题描述】:

我有一个字节数据数组,但是这个数组可以保存任何无符号数(字节/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;
}

或者我真的最好离开作为单独的功能并维护它们?

【问题讨论】:

    标签: c# generics


    【解决方案1】:

    您需要使用 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 缓冲区中,我们必须假设它可以容纳足够的字节。

    【讨论】:

    • 既然这么琐碎,你为什么不发布你的答案。
    • 我看不到“直截了当”的方面。 :\ 我已经更新了我的问题。
    • 点了。新的和改进的,现在也有直接的代码 :) 我第二个关于装箱/拆箱性能的 Nafals 点,并且可能更愿意去原来的实现。至少在可能的类型数量有限的情况下。
    • 这段代码不会编译,运行时会崩溃,即使T是ulong。如果 T 是 double 或其他浮点类型怎么办?
    • 只是说这个答案也有效,谢谢!我不想要一个我可以帮助它的长缓冲区——在 if/else 条件下有几个额外的操作比需要的从头开始好一点:)
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      遗憾的是,您最终将不得不为要反序列化的每种类型编写一个函数。这是因为您不能在 T 上真正使用逻辑运算符(&、| 等)或数学运算符(=、-、* 等)。此外,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% 理想的,但它会起作用。我用过类似的方法,发现在到处添加一些类型之后,我在不知不觉中就支持了所有的内在类型。

      【讨论】:

      • 我喜欢这种设计方法,但它不会编译,因为ReadInt(startIndex); 会返回一个 int(不是 T)而ReadByte(startIndex); 会返回一个字节(不是 T)。您也不能转换为 T,因为编译器不知道如何将 int 或 byte 转换为 T。
      • 是的,抱歉,我会更新代码。我刚刚从头顶炸开了一些东西。
      猜你喜欢
      • 2015-03-02
      • 2010-11-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-24
      • 2023-03-20
      相关资源
      最近更新 更多