Метод, который может соответствовать тому, что вы пытаетесь сделать, это 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)
{
// Make sure the array won't be moved around by the GC
var handle = GCHandle.Alloc(output, GCHandleType.Pinned);
var destination = (byte*)handle.AddrOfPinnedObject().ToPointer();
var byteLength = length * sizeInBytes;
// There are faster ways to do this, particularly by using wider types or by
// handling special lengths.
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 для справки)
person
Jérôme Laban
schedule
03.10.2009