简体   繁体   English

如何从字节数组转换为通用数组?

[英]How can I convert from a Byte Array to Generic Array?

Disclaimer - Since I have a working solution, this question perhaps crosses the line to code review, however I'm convinced I'm reinventing the wheel and a better solution exists.免责声明- 由于我有一个可行的解决方案,这个问题可能越界到代码审查,但我确信我正在重新发明轮子并且存在更好的解决方案。

Context语境

I am working with a low level communication protocol whereby I receive a byte[] as a serialized array of a known type.我正在使用一个低级通信协议,我接收一个byte[]作为已知类型的序列化数组。 The data type will always be an unmanaged value type, typically UInt16 , char etc.数据类型将始终是非unmanaged值类型,通常是UInt16char等。

Question问题

How can I (should I) generically convert from a byte[] to a T[] so as not to provide implementation for each case, or type specific converters?我如何(应该)一般地从byte[]转换为T[]以便不为每种情况提供实现,或类型特定的转换器?

Working Code工作代码

I have written an Extension Method, ToArray<T> , on byte[] :我在byte[]上编写了一个扩展方法ToArray<T>

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

This also depends on another small extension, Slice<T> , on T[] :这还取决于T[]上的另一个小扩展Slice<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;
}

Test Case测试用例

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

Output Output

10000
20000
30000

Honestly, if this was me: I wouldn't get it as an array - I'd simply coerce between spans.老实说,如果这是我:我不会把它当作一个数组 - 我只是在跨度之间强制。 An array is implicitly convertible to a span, so the input doesn't change.数组可以隐式转换为跨度,因此输入不会改变。 Span as an output is a different API, but very comparable in all ways except one (storage as a field).作为 output 的跨度是不同的 API,但在所有方面都非常相似,除了一个(作为字段存储)。

Consider考虑

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

This is zero allocation and zero processing - it simply reinterprets the span over the existing data, which means it is fundamentally doing exactly what BitConverter does behind the scenes.这是零分配和零处理——它只是重新解释了现有数据的跨度,这意味着它基本上在做BitConverter在幕后所做的事情。 There's also the concept of ReadOnlySpan<T> if the consumer needs to read but doesn't need to be able to write to the data.如果消费者需要读取但不需要能够写入数据,则还有ReadOnlySpan<T>的概念。 And spans allow you to work on portions of an array without needing to convey the bounds separately.跨度允许您处理数组的某些部分,而无需单独传达边界。

And if you can't use spans as the return, you can still use this approach for the code:如果你不能使用 span 作为返回,你仍然可以对代码使用这种方法:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM