[英]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.免责声明- 由于我有一个可行的解决方案,这个问题可能越界到代码审查,但我确信我正在重新发明轮子并且存在更好的解决方案。
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
值类型,通常是UInt16
、 char
等。
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[]
以便不为每种情况提供实现,或类型特定的转换器?
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;
}
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.