Эмулировать передачу по ссылке в C ++ / CLI без использования отслеживания ссылки (%)? - PullRequest
0 голосов
/ 08 января 2010

Я хочу эмулировать следующий метод:

ref class Something{
   void foo(array<double>^% data)
   {
      data = gcnew array<double>(10);
   }
};

Так, что массив вызывающей стороны модифицируется / создается. Тем не менее, есть некоторые ограничения:

  1. foo не может использовать ref / out /% и никакие дополнительные параметры.
  2. фактический параметр foo должен быть Object ^.

Можно ли сделать это с помощью неуправляемых указателей, IntPtr или более неясным способом?

Ответы [ 2 ]

2 голосов
/ 08 января 2010

Нет. Если фактический параметр foo является дескриптором объекта, нет способа изменить ссылку на вызывающий стек.

Вам необходимо использовать ссылку отслеживания, чтобы иметь возможность влиять на дескриптор, передаваемый в ваш метод.


Редактировать в ответ на комментарий AZ:

Причина, по которой это не работает, заключается в том, что когда вы передаете дескриптор объекта, вы фактически ничего не передаете, кроме местоположения объекта в памяти. По своей сути это просто целое число, содержащее ячейку памяти.

Когда вы назначаете новый объект в вашем методе, вы переопределяете это местоположение (помещая новое целое число на место). Это не меняет оригинальный дескриптор вызывающего, так как это просто копия. Это нормальный способ передачи параметров.

Используя ссылку отслеживания, вы передаете ссылку на местоположение в памяти. Это похоже на передачу указателя в C или C ++. Когда вы сделаете это, вы можете изменить местоположение, на которое указывает ссылка (это значение), не изменяя саму ссылку - только изменяя то, на что она ссылается.

1 голос
/ 08 января 2010
  1. foo не может использовать ref / out /% и никакие дополнительные параметры.
  2. фактический параметр foo должен быть Object ^.

Вызывающий может выделить массив и передать его, и вы можете изменить элементы в массиве - но это просто приведение массива к Object и обратно.

Если вы хотите изменить фактический массив - например, назначить его новому массиву другой длины, без использования ref (% в C ++ / CLI) или out ([Out] в C ++ / CLI) , ты не сможешь это сделать.

И вот почему:

. Виртуальная машина .NET работает, помещая вещи в стек. То, как вы передаете параметры в функции, работает за кадром:

  1. Вы помещаете параметры (в данном случае, ссылку на массив) в стек.
  2. Вы вызываете функцию
  3. ВМ настраивается для вызова метода, считывая эти значения из стека в указанном порядке и присваивая их локальным переменным в вашей функции.
  4. Код внутри функции выполняется.

Если вызывающая сторона не помещает действительные данные в стек до вызова функции, происходит сбой. Если вызывающая сторона помещает данные в неправильном порядке - происходит сбой. Если что-нибудь смешно со стеком - он вылетает.

Кроме того, методы не могут изменять вещи, которые уже находятся в стеке. (Если бы они это сделали, они бы испортили стек - он рушится), они могут только вставлять и извлекать новые вещи из стека.

Это означает 2 вещи:

  1. Чтобы вызвать функцию, вы должны поместить ссылку на массив в стек. Поэтому вызывающая сторона должна выделить сам массив или передать нулевую ссылку.

  2. Вы не можете изменить ссылку на массив, поэтому функция не может предоставить новый массив. Поскольку массивы по своей природе не могут быть изменены, это означает, что вы не можете добавлять новые элементы (однако вы можете изменять существующие элементы)

Отказ от ответственности: Эрик Липперт, вероятно, придет и объяснит, почему все, что я только что написал, неверно, но, насколько мне известно, это то, как это работает

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