С#:в общем преобразовать неуправляемый массив в управляемый список
Вопрос
Я имею дело с набором собственных функций, которые возвращают данные через динамически выделяемые массивы.Функции принимают на вход указатель ссылки, а затем указывают его на результирующий массив.
Например:
typedef struct result
{
//..Some Members..//
}
int extern WINAPI getInfo(result**);
После вызова '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() в данном случае не принесло никакой пользы.
Что мне не хватает?Может ли кто-нибудь предложить общий подход к этой проблеме?
Решение
Вы можете сделать разумное предположение, что размер и представление всех указателей одинаковы (не уверен, гарантирует ли это спецификация 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*
всякий раз, когда вы это вызываете, но, по крайней мере, в противном случае дублирования кода не будет.
Другие советы
Вы сказали:
Marshal.copy () должен знать размер неуправляемого массива.Я мог бы определить это только с помощью небезопасного кода
Кажется, тебя не хватает Маршал.SizeOf().
Судя по тому, что вы упомянули в посте, этого может быть достаточно для решения вашей проблемы.(Кроме того, параметром вашей функции может быть Object** вместо T**.)