我正在开发一个C#类,其中我需要能够接受一个字节数组并将其复制到相同大小的通用变量.在C/C++这样的事情(复制)会很容易,但在C#中并没有那么多. MyClassT{ public T Value = default(T); public M
MyClass<T> { public T Value = default(T); public MyClass(byte[] bytes) { // how to copy `bytes` into `Value`? } }
我宁愿不使用拳击.有没有办法使用编组,反射或非托管/不安全代码?
我确实找到了this other post,但唯一建议的答案是行不通的,因为它使用拳击.
如果您使用的是最新的.NET,则可以使用Span< T> (System.Buffers)为此:class MyClass<T> where T : struct { public T Value = default(T); public MyClass(byte[] bytes) { Value = MemoryMarshal.Cast<byte, T>(bytes)[0]; } }
您还可以在最近的C#版本中使用unsafe(对于T:非托管约束):
class MyClass<T> where T : unmanaged { public T Value = default(T); public unsafe MyClass(byte[] bytes) { fixed (byte* ptr = bytes) { Value = *(T*)ptr; // note: no out-of-range check here; dangerous } } }
你也可以使用Unsafe.*方法(System.Runtime.CompilerServices.Unsafe)做一些事情.例如(注意没有约束):
class MyClass<T> { public T Value = default(T); public unsafe MyClass(byte[] bytes) { T local = default(T); fixed (byte* ptr = bytes) { Unsafe.Copy(ref local, ptr); // note: no out-of-range check here; dangerous } Value = local; } }
如果要检查超出范围的问题:
if (bytes.Length < Unsafe.SizeOf<T>()) throw new InvalidOperationException("Not enough data, fool!");
或者如果你有T:unmanaged约束,你可以使用sizeof(T).您不需要使用Span< T>解决方案(第一个),因为原始的Cast< byte,T>在这种情况下,将产生一个长度为零的跨度,[0]将适当地抛出.
我认为这也应该有效!
public unsafe MyClass(byte[] bytes) { Value = Unsafe.As<byte, T>(ref bytes[0]); // note: no out-of-range check here; dangerous }
完整示例(适用于net462):
using System; using System.Runtime.CompilerServices; struct Foo { public int x, y; } class MyClass<T> { public T Value = default(T); public unsafe MyClass(byte[] bytes) { if (bytes.Length < Unsafe.SizeOf<T>()) throw new InvalidOperationException("not enough data"); Value = Unsafe.As<byte, T>(ref bytes[0]); } } static class P { static void Main() { byte[] bytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }; var obj = new MyClass<Foo>(bytes); var val = obj.Value; Console.WriteLine(val.x); // 67305985 = 0x04030201 Console.WriteLine(val.y); // 134678021 = 0x08070605 } }