La méthode qui pourrait correspondre à ce que vous essayez de faire est Marshal.Copy, mais il n'a pas prenez les paramètres appropriés pour faire une méthode générique.
Bien qu'il ne soit pas possible d'écrire une méthode générique avec des contraintes génériques qui pourraient décrire ce qui est possible, tous les types ne peuvent pas être copiés en utilisant une méthode "dangereuse". Il y a quelques exceptions; les classes sont l'un d'entre eux.
Voici un exemple de code:
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);
}
}
La méthode peut être générique, mais il ne peut pas prendre un pointeur d'un type générique. Ce n'est pas un problème puisque la covariance des pointeurs aide, mais cela a malheureusement pour effet d'empêcher une résolution implicite du type d'argument générique. Vous devez ensuite spécifier explicitement MakeArray.
J'ai ajouté un cas spécial pour les structures, où il est préférable d'avoir des types qui spécifient un struct layout. Cela peut ne pas être un problème dans votre cas, mais si les données du pointeur proviennent du code C ou C++ natif, il est important de spécifier un type de disposition (le CLR peut choisir de réorganiser les champs pour mieux aligner la mémoire).
Mais si le pointeur provient exclusivement de données générées par du code managé, vous pouvez supprimer la vérification. En outre, si la performance est un problème, il existe de meilleurs algorithmes pour copier les données que de le faire octet par octet. (Voir les innombrables implémentations de memcpy pour référence)
Cela ne fonctionne pas parce que T peut ne pas être une struct-byte, int, long, etc – wj32
Regarde près mais je ne vois pas le pointeur incrémenter - il sera toujours utilise le premier octet.Aussi, y a-t-il une raison pour void *? –
'struct' signifie type de valeur, donc int, byte etc sont valides. Et vous ne pouvez pas avoir un pointeur vers un type de valeur none, donc struct est le meilleur que vous pouvez faire. – samjudson