法のような何かを行うことができますが、それはしていません適切なパラメータを取って一般的な方法を作ります。
可能なことを説明できる汎用的な制約を持つジェネリックメソッドを記述することはできませんが、すべてのタイプを「安全でない」方法でコピーすることはできません。いくつかの例外があります。クラスはこれらの1つです。ここで
はサンプルコードです:
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を明示的に指定する必要があります。
構造体に特殊なケースが追加されました。ここでは、struct layoutを指定するタイプが最も適しています。これは問題ではないかもしれませんが、ポインターデータがネイティブのCまたはC++コードから来ている場合は、レイアウトの種類を指定することが重要です(CLRはフィールドの順序を調整してメモリの整列を改善することがあります)。
しかし、ポインタが排他的にマネージコードによって生成されたデータから来ている場合、チェックを削除することができます。
また、パフォーマンスが問題になる場合は、バイトごとに行うよりもデータをコピーするアルゴリズムが優れています。 (参照のためにmemcpyの無数の実装を参照してください)
Tがstruct-byte、int、longなどでないかもしれないので、これはうまくいきません。 – wj32
ポインタは増加していますが、最初のバイトを使用します。また、void *の理由はありますか? –
'struct'は値の型を意味するので、int、byteなどが有効です。そして、あなたはnone型の型へのポインタを持つことができないので、構造体はあなたができる最高です。 – samjudson