2009-06-12 14 views
2

への汎用ポインタに変換私はbyte[]byte*を変換したいのですが、私もこれを行うには、再利用可能な機能を持つようにしたい:C#の:配列

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関数がありますか?何か案は?

答えて

5

法のような何かを行うことができますが、それはしていません適切なパラメータを取って一般的な方法を作ります。

可能なことを説明できる汎用的な制約を持つジェネリックメソッドを記述することはできませんが、すべてのタイプを「安全でない」方法でコピーすることはできません。いくつかの例外があります。クラスはこれらの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の無数の実装を参照してください)

0

次はうまくいくかどうかは全く分かりませんが、それは(少なくとも、それは:)コンパイルされることがあります

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を使用することです。

+0

Tがstruct-byte、int、longなどでないかもしれないので、これはうまくいきません。 – wj32

+0

ポインタは増加していますが、最初のバイトを使用します。また、void *の理由はありますか? –

+3

'struct'は値の型を意味するので、int、byteなどが有効です。そして、あなたはnone型の型へのポインタを持つことができないので、構造体はあなたができる最高です。 – samjudson

1

質問は次のようになります。単純型を汎用型に指定する方法。

unsafe void Foo<T>() : where T : struct 
{ 
    T* p; 
} 

はエラーを与える:
は、のアドレスを取るのサイズを取得、または管理タイプ(「T」)これについて

1

どのようにポインタを宣言することができませんか?

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)を使用することはできませんが、呼び出し元はMarshal.Copyで何をやろうとしている一致する可能性が

byte[] b = MakeArray<byte>(pBytes, lenBytes, sizeof(byte)); 
+0

Marshal.SizeOf(typeof(T))を使用できます。サイズを取得する。 – SoLaR