c#: общо конвертиране на неуправляван масив в управляван списък

Имам работа с набор от собствени функции, които връщат данни чрез динамично разпределени масиви. Функциите приемат референтен указател като вход, след което го насочват към получения масив.

Например:

typedef struct result
{
   //..Some Members..//
}

int extern WINAPI getInfo(result**);

След извикването „резултат“ сочи към масив от резултат*, завършващ с нула.

Искам да създам управляван списък от този неуправляван масив. Мога да направя следното:

struct Result
{
   //..The Same Members..//
}

public static unsafe List<Result> getManagedResultList(Result** unmanagedArray)
{
    List<Result> resultList = new List<Result>();

    while (*unmanagedArray != null)
    {
       resultList.Add(**unmanagedArray);
       ++unmanaged;
    }
    return result;
}

Това работи, ще бъде досадно и грозно да се прилага повторно за всеки тип структура, с която ще трябва да се справя (~35). Бих искал решение, което е общо за типа на структурата в масива. За тази цел опитах:

public static unsafe List<T> unmanagedArrToList<T>(T** unmanagedArray)
{ 
    List<T> result = new List<T>();
    while (*unmanagedArray != null)
    {
        result.Add((**unmanagedArray));
        ++unmanagedArray;
    }
    return result;
}

Но това няма да се компилира, защото не можете да "вземете адреса на, да получите размера или да декларирате указател към управляван тип ('T')".

Също така се опитах да направя това, без да използвам опасен код, но се натъкнах на проблема, че Marshal.Copy() трябва да знае размера на неуправлявания масив. Можех да определя това само с помощта на опасен код, така че изглеждаше, че няма полза от използването на Marshal.Copy() в този случай.

какво ми липсва Може ли някой да предложи общ подход към този проблем?


person Odrade    schedule 10.06.2009    source източник


Отговори (2)


Можете да направите разумно предположение, че размерът и представянето на всички указатели са еднакви (не съм сигурен дали спецификацията на C# гарантира това, но на практика ще откриете, че е така). Така че можете да третирате своя T** като IntPtr*. Също така, не виждам как Marshal.Copy ще ви помогне тук, тъй като има само претоварвания за вградени типове. Така:

public static unsafe List<T> unmanagedArrToList<T>(IntPtr* p)
{ 
    List<T> result = new List<T>();
    for (; *p != null; ++p)
    {
        T item = (T)Marshal.PtrToStructure(*p, typeof(T));
        result.Add(item);
    }
    return result;
}

Разбира се, ще имате нужда от изрично прехвърляне към IntPtr* всеки път, когато извиквате това, но поне няма дублиране на код иначе.

person Pavel Minaev    schedule 10.06.2009

Ти каза:

Marshal.Copy() трябва да знае размера на неуправлявания масив. Мога да определя това само с помощта на опасен код

Изглежда, че ви липсва Marshal.SizeOf ().

От това, което споменахте в публикацията, това може да е достатъчно, за да реши проблема ви. (Също така може да се наложи параметърът на вашата функция да бъде Object** вместо T**.)

person John Fisher    schedule 10.06.2009
comment
Marshal.Copy трябва да знае колко елемента има в изходния масив, но не знам това по време на компилиране, тъй като масивът се разпределя динамично от собствен код. Не знам никакъв начин да определя броя на елементите в неуправлявания масив, без да използвам опасен код. - person Odrade; 10.06.2009
comment
Освен това обектът е управляван тип. Както беше посочено по-горе, не можете да извършвате опасни операции върху управляван тип. - person Odrade; 10.06.2009
comment
В най-лошия случай можете да използвате IntPtr за параметъра на вашата функция, след което да извикате Marshal.Copy() веднъж за всеки елемент в масива, докато стигнете до края. Трябва да можете да направите пряк път това, като направите копие на предадения IntPtr, като използвате Marshal.SizeOf(), за да определите правилния размер, отколкото да добавите този размер в цикъл, докато намерите нулевия запис. - person John Fisher; 10.06.2009