簡體   English   中英

如何從字節數組轉換為通用數組?

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

免責聲明- 由於我有一個可行的解決方案,這個問題可能越界到代碼審查,但我確信我正在重新發明輪子並且存在更好的解決方案。

語境

我正在使用一個低級通信協議,我接收一個byte[]作為已知類型的序列化數組。 數據類型將始終是非unmanaged值類型,通常是UInt16char等。

問題

我如何(應該)一般地從byte[]轉換為T[]以便不為每種情況提供實現,或類型特定的轉換器?

工作代碼

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

這還取決於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;
}

測試用例

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

10000
20000
30000

老實說,如果這是我:我不會把它當作一個數組 - 我只是在跨度之間強制。 數組可以隱式轉換為跨度,因此輸入不會改變。 作為 output 的跨度是不同的 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();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM