在C#中,我需要将T []写入流,理想情况下不需要任何额外的缓冲区。我有一个动态代码,将T [](其中T是无对象结构)转换为void *并将其修复到内存中,并且效果很好。当流是文件时,我可以使用本机Windows API直接传递void *,但现在我需要写一个带有byte []的通用Stream对象。
问题:任何人都可以建议一种黑客方法来创建一个虚拟数组对象,它实际上没有任何堆分配,而是指向已经存在的(和固定的)堆位置?
这是我需要的伪代码:
void Write(Stream stream, T[] buffer)
{
fixed( void* ptr = &buffer ) // done with dynamic code generation
{
int typeSize = sizeof(T); // done as well
byte[] dummy = (byte[]) ptr; // <-- how do I create this fake array?
stream.Write( dummy, 0, buffer.Length*typeSize );
}
}
更新的
我在this article中描述了如何深入fixed(void* ptr=&buffer)
。我总是可以创建一个byte [],将其修复到内存中并从一个指针到另一个指针执行不安全的字节复制,然后将该数组发送到流中,但我希望避免不必要的额外分配和复制。
不可能? 进一步思考后,byte []在堆中有一些元数据,包括数组维度和元素类型。简单地将引用(指针)传递给T []作为byte []可能不起作用,因为块的元数据仍然是T []的元数据。即使元数据的结构相同,T []的长度也会远小于byte [],因此托管代码对byte []的任何后续访问都会产生错误的结果。
要求的功能@ Microsoft Connect 请投票给this request,希望MS能听。
答案 0 :(得分:3)
这种代码永远不会以通用方式工作。它依赖于一种硬假设,即T的内存布局是可预测且一致的。只有在T是简单值类型时才会出现这种情况。暂时忽略字节序。如果T是引用类型,你就会死在水中,你将复制永远无法反序列化的跟踪句柄,你必须给T结构约束。
但这还不够,结构类型也不可复制。即使他们没有引用类型字段,也不能约束。内部布局由JIT编译器确定。它在闲暇时交换字段,选择字段正确对齐的字段,结构值采用最小存储大小。您将序列化的值只能由运行完全相同的CPU架构和JIT编译器版本的程序正确读取。
框架中已经有很多类可以完成你正在做的事情。最接近的匹配是.NET 4.0 MemoryMappedViewAccessor类。它需要做同样的工作,在内存映射文件中提供原始字节。主要的是System.Runtime.InteropServices.SafeBuffer类,看看有了Reflector。不幸的是,你不能只复制类,它依赖于CLR来进行转换。再说一遍,它还有一个星期才可用。
答案 1 :(得分:0)
因为stream.Write无法获取指针,所以无法避免复制内存,因此会有一些减速。您可能需要考虑使用BinaryReader和BinaryWriter来序列化您的对象,但这里的代码可以让您按照自己的意愿行事。请记住,T的所有成员也必须是结构。
unsafe static void Write<T>(Stream stream, T[] buffer) where T : struct
{
System.Runtime.InteropServices.GCHandle handle = System.Runtime.InteropServices.GCHandle.Alloc(buffer, System.Runtime.InteropServices.GCHandleType.Pinned);
IntPtr address = handle.AddrOfPinnedObject();
int byteCount = System.Runtime.InteropServices.Marshal.SizeOf(typeof(T)) * buffer.Length;
byte* ptr = (byte*)address.ToPointer();
byte* endPtr = ptr + byteCount;
while (ptr != endPtr)
{
stream.WriteByte(*ptr++);
}
handle.Free();
}
答案 2 :(得分:0)
查看我对相关问题的回答: What is the fastest way to convert a float[] to a byte[]?
在其中我暂时将一个浮点数组转换为一个字节数组而没有内存分配和复制。 为此,我使用内存操作更改了CLR的元数据。
不幸的是,这种解决方案不适合仿制药。但是,您可以将此黑客与代码生成技术结合起来解决您的问题。
答案 3 :(得分:0)
查看这篇文章Inline MSIL in C#/VB.NET and Generic Pointers获取梦想代码的最佳方式:)