У меня есть такой класс
public unsafe class EigenSolver
{
public double* aPtr
{get; private set;}
public EigenSolver(double* ap)
{
aPtr = ap;
}
public EigenSolver(double[] aa)
{
// how to convert from aa double array to pointer?
}
public void Solve()
{
Interop.CallFortranCode(aPtr);
}
}
Как можно предположить, я должен преобразовать из double
выстройте к указателю. Как сделать это?
Примечание: функция interop Interop.CallFortranCode(double* dPtr)
что-то, что я не могу изменить.
Примечание 2: Оба конструкторы необходимы, потому что некоторые мои пользователи API хотят передать в указателях, и некоторые хотели бы передать в массиве. Я не могу вынудить их выбрать.
Используйте оператор fixed
:
fixed (double* aaPtr = aa) { // You can use the pointer in here. }
В контексте fixed
память для вашей переменной закрепляется, поэтому сборщик мусора не будет пытаться ее перемещать.
Вместо этого я бы использовал следующий подход:
public class EigenSolver { public double[] _aa; /* There really is no reason to allow callers to pass a pointer here, just make them pass the array. public EigenSolver(double* ap) { aPtr = ap; } */ public EigenSolver(double[] aa) { _aa = aa; } public void Solve() { unsafe { fixed (double* ptr = _aa) { Interop.CallFortranCode(ptr); } } } }
Это предполагает, конечно, что CallFortranCode не пытается использовать указатель вне вызова. Как только фиксированный оператор выходит за пределы области видимости, указатель больше не действителен...
UPDATE:
Вы никак не можете взять адрес аргумента double[] aa
и сохранить его в поле экземпляра. Даже если компилятор позволит вам это сделать, GC обязательно переместит эту память, оставив ваш указатель бесполезным.
Вы, вероятно, можете сделать это: Использовать Marshal.AllocHGlobal для выделения памяти, достаточной для хранения всех элементов массива (aa.Length * sizeof(double))
). Затем используйте Marshal.Copy для копирования содержимого массива в только что выделенную память:
bool _ownsPointer; public EigenSolver(double[] aa) { IntPtr arrayStore = (double*)Marshal.AllocHGlobal(aa.Length * sizeof(double)); Marshal.Copy(aa, 0, arrayStore, aa.Length); this.aPtr = (double*)arrayStore.ToPointer(); _ownsPointer = true; } ~EigenSolver { if (_ownsPointer) { Marshal.FreeHGlobal(new IntPtr(this.aPtr)); } }
Надеюсь, это сработает...
Andrew
Хотя исправлено
действительно является тем ответом, который вы ищете, вы не можете использовать его так, как пытаетесь . Как только блок fixed
заканчивается (в закрывающей фигурной скобке), указатель становится недействительным. Таким образом, вы не можете использовать его в конструкторе для хранения указателя. И вам не следует этого делать, потому что закрепление управляемого объекта в памяти на длительное время приводит к резкому снижению производительности.
Вместо этого я бы передал массив (как управляемый объект) вашей функции Solve
. Таким образом, вы можете закрепить его, просто чтобы сделать вызов взаимодействия с fortran, а затем позволить контроллеру управления этим позаботиться.