2

byte*aを aに変換したいの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 関数はありますか? 何か案は?

4

5 に答える 5

5

あなたがやろうとしていることに一致する可能性のあるメソッドはMarshal.Copyですが、ジェネリックメソッドを作成するための適切なパラメーターを取りません。

何が可能かを説明できるジェネリック制約を使用してジェネリックメソッドを作成することはできませんが、「安全でない」方法を使用してすべての型をコピーできるわけではありません。いくつかの例外があります。クラスはこれらの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を明示的に指定する必要があります。

構造体の特殊なケースを追加しました。構造体のレイアウトを指定する型を使用するのが最適です。これはあなたの場合は問題ではないかもしれませんが、ポインタデータがネイティブCまたはC ++コードからのものである場合、レイアウトの種類を指定することが重要です(CLRは、メモリの配置を改善するためにフィールドを並べ替えることを選択する場合があります)。

ただし、ポインターがマネージコードによって生成されたデータからのみ取得されている場合は、チェックを削除できます。

また、パフォーマンスが問題になる場合は、バイトごとにデータをコピーするよりも、データをコピーする方が優れたアルゴリズムがあります。(参照用にmemcpyの無数の実装を参照してください)

于 2009-10-03T00:49:01.377 に答える
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)を使用できませんが、呼び出し元は次のようなことを行うことができます。

byte[] b = MakeArray<byte>(pBytes, lenBytes, sizeof(byte));
于 2009-10-01T14:11:56.037 に答える
1

問題は次のようになるようです:ジェネリック型を単純型に指定する方法。

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

次のエラーが表示されます:
マネージド型 ('T') のアドレスを取得したり、サイズを取得したり、ポインタを宣言したりできません

于 2009-06-12T09:18:54.917 に答える