c# - 如何将字节数组转换为通用数组?

标签 c# generics type-conversion bitconverter

免责声明 - 因为我有一个可行的解决方案,所以这个问题可能会越界到代码审查,但我确信我正在重新发明轮子并且存在更好的解决方案。

上下文

我正在使用低级通信协议(protocol),因此我收到了一个 byte[]作为已知类型的序列化数组。数据类型始终是 unmanaged值类型,通常为 UInt16 , char

问题

我如何(应该)从 byte[] 进行一般转换?到 T[]以免为每种情况提供实现,或键入特定的转换器?

工作代码

我写了一个扩展方法,ToArray<T> , 关于 byte[] :

public static T[] ToArray<T>(this byte[] input)
    where T: unmanaged
{
    // Use Reflection to find the appropiate MethodInfo from BitConverter
    var converterMethod =  (from method in typeof(BitConverter).GetMethods()
                            // Double redundant selection
                            where ((method.ReturnType == typeof(T)) && (method.Name == $"To{typeof(T).Name}"))
                            select method).FirstOrDefault();
            
    // Create a Function delegate from the MethodInfo, since all BitConverter.To methods share a signiture
    var converter = converterMethod.CreateDelegate(typeof(Func<byte[], int, T>));

    // Some meta variables regarding the target type
    int typeSize = Marshal.SizeOf<T>();
    int count = input.Length / typeSize;
            
    // Error Checking - Not yet implmented
    if (input.Length % typeSize != 0) throw new Exception();
            
    // Resulting array generation
    T[] result = new T[count];
    for(int i = 0; i < count; i++)
    {
        result[i] = (T)converter.DynamicInvoke(
            input.Slice(i * typeSize, typeSize), 0);
    }
    return result;
}

这也依赖于另一个小扩展,Slice<T> , 关于 T[] :

public static T[] Slice<T>(this T[] array, int index, int count)
{
    T[] result = new T[count];
    for (int i = 0; i < count; i++) result[i] = array[index + i];
    return result;
}

测试用例

class Program
{
    static void Main(string[] args)
    {
        byte[] test = new byte[6]
        {
            0b_0001_0000, 0b_0010_0111, // 10,000 in Little Endian
            0b_0010_0000, 0b_0100_1110, // 20,000 in Little Endian
            0b_0011_0000, 0b_0111_0101, // 30,000 in Little Endian
        };

        UInt16[] results = test.ToArray<UInt16>();

        foreach (UInt16 result in results) Console.WriteLine(result);
    }
}

输出

10000
20000
30000

最佳答案

老实说,如果这是我:我不会把它作为一个数组 - 我只是在跨度之间进行强制转换。数组可以隐式转换为跨度,因此输入不会改变。 Span 作为输出是一个不同 API,但在所有方面都非常相似,除了一个(存储作为字段)。

考虑

public static Span<T> Coerce<T>(this byte[] input)
    where T: unmanaged
    => MemoryMarshal.Cast<byte, T>(input);

这是零分配和零处理 - 它只是重新解释现有数据的跨度,这意味着它从根本上做的正是 BitConverter在幕后做。还有 ReadOnlySpan<T> 的概念如果消费者需要读取但不需要能够写入数据。跨度允许您处理数组的部分,而无需单独传达边界。

如果您不能使用 span 作为返回值,您仍然可以对代码使用这种方法:

public static T[] Convert<T>(this byte[] input)
    where T: unmanaged
    => MemoryMarshal.Cast<byte, T>(input).ToArray();

关于c# - 如何将字节数组转换为通用数组?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65264969/

相关文章:

c# - 如何获取类型的默认泛型委托(delegate)

string - 在 Go 中,为什么在转换为字符串时不使用 stringer 接口(interface)?

python - 如何在 Python 中将带点和逗号的字符串转换为 float

c# - 在应用程序之外缓存数据

c# - 如何在 Entity Framework 6 中将 System.Version 映射为复杂类型

c# - 非序列化无效

c++ - wchar_t* 到字符串之间的转换

c# - 褪色的正确数学是什么?

java - 列表泛型协方差

Java 泛型 ?扩展字符串