문제

나는 변환하고 싶다 byte* 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 기능이 내장되어 있습니까? 어떤 아이디어?

도움이 되었습니까?

해결책

당신이하려는 일에 맞는 방법은 다음과 같습니다. 마샬, 그러나 일반적인 방법을 만들기 위해 적절한 매개 변수를 사용하지 않습니다.

가능한 것이 무엇인지 설명 할 수있는 일반적인 제약으로 일반적인 방법을 작성할 수는 없지만, "안전하지 않은"방법을 사용하여 모든 유형을 복사 할 수있는 것은 아닙니다. 몇 가지 예외가 있습니다. 수업 중 하나입니다.

다음은 샘플 코드입니다.

    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;
}

여기서는 크기를 사용할 수 없지만 발신자는 다음과 같은 것을 할 수 있습니다.

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 올바른 유형으로 변환합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top