質問
を変換したいです byte*
に byte[]
, 、しかし、これを行うための再利用可能な関数も必要です。
public unsafe static T[] Create<T>(T* ptr, int length)
{
T[] array = new T[length];
for (int i = 0; i < length; i++)
array[i] = ptr[i];
return array;
}
残念ながら、T が「.NET マネージド型」である可能性があり、ポインターを持たないため、コンパイラ エラーが発生します。 それらの. 。さらにイライラするのは、T を「アンマネージ型」に制限できるジェネリック型制約がないことです。これを行うための組み込みの .NET 関数はありますか?何か案は?
解決
あなたがやろうとしていることに一致する可能性のある方法は、 マーシャル.コピー, ですが、ジェネリック メソッドを作成するための適切なパラメーターは必要ありません。
そこでは、何が可能かを記述する一般的な制約を持つジェネリック メソッドを記述することはできませんが、すべての型を「安全でない」方法でコピーできるわけではありません。いくつかの例外があります。クラスもそのひとつです。
サンプルコードは次のとおりです。
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 の無数の実装を参照してください)
他のヒント
問題になると思われます。
unsafe void Foo<T>() : where T : struct
{
T* p;
}
は、エラーを与える:
、のアドレスを取るのサイズを取得、または管理タイプ(「T」)
これはどう?
static unsafe T[] MakeArray<T>(void* t, int length, int tSizeInBytes) where T:struct
{
T[] result = new T[length];
for (int i = 0; i < length; i++)
{
IntPtr p = new IntPtr((byte*)t + (i * tSizeInBytes));
result[i] = (T)System.Runtime.InteropServices.Marshal.PtrToStructure(p, typeof(T));
}
return result;
}
私たちはここにはsizeof(T)を使用することはできませんが、呼び出し側は、
のような何かを行うことができますbyte[] b = MakeArray<byte>(pBytes, lenBytes, sizeof(byte));
働くだろう以下の場合、私は全く分かりませんが、それは(少なくともそれがコンパイルされます):かもしれません。
public unsafe static T[] Create<T>(void* ptr, int length) where T : struct
{
T[] array = new T[length];
for (int i = 0; i < length; i++)
{
array[i] = (T)Marshal.PtrToStructure(new IntPtr(ptr), typeof(T));
}
return array;
}
キーが正しい型に変換するMarshal.PtrToStructure
を使用することである。