您需要的方法可能是
Marshal.Copy,但它不会使用合适的参数来生成一个通用方法。
尽管不可能使用泛型约束编写描述可能性的通用方法,但并非每种类型都可以使用“不安全”的方式进行复制。有一些例外情况;类就是其中之一。
以下是示例代码:
public unsafe static T[] Create<T>(void* source, int length)
{
var type = typeof(T);
var sizeInBytes = Marshal.SizeOf(typeof(T));
T[] output = new T[length];
if (type.IsPrimitive)
{
var handle = GCHandle.Alloc(output, GCHandleType.Pinned);
var destination = (byte*)handle.AddrOfPinnedObject().ToPointer();
var byteLength = length * sizeInBytes;
for (int i = 0; i < byteLength; i++)
destination[i] = ((byte*)source)[i];
handle.Free();
}
else if (type.IsValueType)
{
if (!type.IsLayoutSequential && !type.IsExplicitLayout)
{
throw new InvalidOperationException(string.Format("{0} does not define a StructLayout attribute", type));
}
IntPtr sourcePtr = new IntPtr(source);
for (int i = 0; i < length; i++)
{
IntPtr p = new IntPtr((byte*)source + i * sizeInBytes);
output[i] = (T)System.Runtime.InteropServices.Marshal.PtrToStructure(p, typeof(T));
}
}
else
{
throw new InvalidOperationException(string.Format("{0} is not supported", type));
}
return output;
}
unsafe static void Main(string[] args)
{
var arrayDouble = Enumerable.Range(1, 1024)
.Select(i => (double)i)
.ToArray();
fixed (double* p = arrayDouble)
{
var array2 = Create<double>(p, arrayDouble.Length);
Assert.AreEqual(arrayDouble, array2);
}
var arrayPoint = Enumerable.Range(1, 1024)
.Select(i => new Point(i, i * 2 + 1))
.ToArray();
fixed (Point* p = arrayPoint)
{
var array2 = Create<Point>(p, arrayPoint.Length);
Assert.AreEqual(arrayPoint, array2);
}
}
该方法可以是通用的,但不能使用通用类型的指针。这不是问题,因为指针协变是有帮助的,但这会不幸地阻止泛型参数类型的隐式解析。因此,您必须明确指定MakeArray。
我已经为结构体添加了一个特殊情况,在这种情况下最好使用指定结构布局的类型。这在您的情况下可能不是问题,但如果指针数据来自本机C或C++代码,则指定布局类型很重要(CLR可能会选择重新排序字段以获得更好的内存对齐)。
但是,如果指针仅来自由托管代码生成的数据,则可以删除检查。
另外,如果性能是一个问题,有比逐字节复制数据更好的算法。(参见无数实现memcpy的参考资料)