Int-указатель на неуправляемый код - PullRequest
4 голосов
/ 21 сентября 2011

Я довольно новичок в C # и у меня простой (?) Вопрос.Из неуправляемого кода я получаю int-указатель:

public foo(ref IntPtr state)
{
   _myState = state;
}

_myState является IntPtr членом класса.Теперь я хочу обмениваться состояниями через _myState с неуправляемым кодом C ++.Все работает, если я напишу это:

public foo(ref IntPtr state)
{
   _myState = state;
   ....do some stuff
   state = 7;
}

В неуправляемом приложении я вижу новое значение 7.Но если я напишу это:

public foo(ref IntPtr state)
{
   _myState = state;
   ...do some stuff
   _myState = 7;
}

, тогда ничего не произойдет.Начальное значение состояния - 0, а при изменении myState на 7 оно не обновляется в неуправляемом приложении.Как я могу назначить переменную-член, такую ​​как _myState, параметру состояния в качестве «указателя», поэтому при обновлении состояния _myState также обновляется?В C ++ это не было бы проблемой с указателем ...

Хорошо, вот реальный код:

[DllExport("QFX_InitializeInterfaceObject", CallingConvention = CallingConvention.StdCall)]
    public static void QFX_InitializeInterfaceObject(
        long windowHandle,
        ref IntPtr processState)
    {
        ChartWindowHandle = (IntPtr)windowHandle;
        OrderCommandProcessState = processState;
    }

Все, что я хочу, это чтобы OrderCommandProcessState получал ту же ссылку, что и processState к его значению.

1 Ответ

12 голосов
/ 21 сентября 2011

Прежде всего, я хочу убедиться, что этот момент ясен: IntPtr - это просто целое число, размер которого совпадает с собственным указателем на архитектуре этого компьютера - это 64-разрядное целое число, например, в системах x64,Он не обязательно имеет семантику указателя, хотя, конечно, для кода взаимодействия характерно вставлять указатели в IntPtrs, чтобы безопасно их распределить.

Переходя к конкретному вопросу, давайте не будем обращать внимания на тот факт, что это IntPtr.Представьте, что это просто int, потому что это в основном то, что он есть:

public void Foo(ref int x) // x is an alias to another variable of type int.
{
    int y = x; // y is a copy of the contents of x
    y = 7; // The contents of y are no longer a copy of the contents of x
}

Изменение y никоим образом не меняет x;x является псевдонимом для другой переменной, а у y кратко есть копия содержимого этой переменной.Это никоим образом не является псевдонимом к той же переменной.

Как я могу превратить одну переменную в псевдоним другой переменной, поэтому, когда состояние одной переменной обновляется, связанная переменнаятоже обновился?В C ++ это не будет проблемой с указателем.

Сегодня в безопасном подмножестве вы можете сделать это только с помощью параметров "ref" и "out" для методов.Параметр ref становится псевдонимом данной переменной. Это единственный безопасный способ, которым вы можете напрямую превратить одну переменную в псевдоним для другой.

CLR также поддерживает локальные ссылки.Мы могли бы реализовать такую ​​функцию, и на самом деле я создал ее на C #.В моем прототипе вы могли бы сказать:

public void Foo(ref int x) // x is an alias to another variable of type int.
{
    ref int y = ref x; // y is now an alias to the same variable that x aliases!
    y = 7; // changing y now changes x, and whatever x 
           // aliases, because those are all the same variable
}

Но мы не добавили эту функцию в C # и не планируем делать это в ближайшее время.Если у вас есть веские аргументы в пользу использования, я хотел бы услышать это.(ОБНОВЛЕНИЕ: функция была добавлена ​​в C # 7.)

(CLR также разрешает типы возврата "ref". Однако CLR НЕ позволяет создавать псевдоним для переменной и затем сохранять этот псевдоним в поле.! Поле может иметь более длительный срок службы, чем связанная переменная, и разработчики CLR хотят избежать всего этого класса ошибок, которые преследуют C и C ++.)

Если вы знаете, что переменная закреплена в определенном местев памяти вы можете отключить систему безопасности и сделать указатель на эту переменную;тогда у вас есть совершенно обычный указатель, который вы можете использовать как в C ++.(То есть, если указатель ptr ссылается на переменную, тогда *ptr является псевдонимом этой переменной.)

unsafe public void Foo(int* x) // *x is an alias to a variable of type int.
{
    int* y = x; // *y is now an alias to the same variable that *x aliases
    *y = 7; // changing *y now changes *x, and whatever *x 
            // aliases, because those are all the same variable
}

В CLR нет ограничений на использование указателей;Вы можете хранить их в полях, если хотите.Однако если вы выключите систему безопасности, тогда вы несете ответственность за то, чтобы сборщик мусора (или любой другой диспетчер памяти, владеющий этим хранилищем - возможно, не управляемая память) не изменилсярасположение переменной с псевдонимом для времени жизни указателя .Не выключайте эту систему безопасности, если вы действительно не знаете, что делаете;эта система безопасности существует, чтобы защитить вас.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...