Мне нравится ответ Cohens, но более техническое определение: Ваш код называет библиотеку. платформа А называет Ваш код . Например, платформа GUI называет Ваш код через обработчики событий. Веб-платформа называет Ваш код через некоторую модель ответа запроса.
Это также называют инверсия управления - внезапно, платформа решает, когда и как выполнить Вас код, а не наоборот как с библиотеками. Это означает, что платформа также оказывает намного большее влияние на то, как необходимо структурировать код.
Они более ограничены. Вы можете сказать ++ на указателе, но не на ref
или out
.
EDIT Некоторая путаница в комментариях, чтобы быть абсолютно ясным: суть вот для сравнения с возможностями указателей. Вы не можете выполнить ту же операцию, что и ptr ++
, на ref
/ out
, то есть сделать так, чтобы он адресовал соседнее место в памяти. Это правда (но здесь не имеет значения), что вы можете выполнить эквивалент (* ptr) ++
, но это будет для сравнения с возможностями значений , а не указателей.
Можно с уверенностью сказать, что они внутренне всего лишь указатели, потому что стек не t перемещается, а C # тщательно организован так, чтобы ref
и out
всегда ссылались на активную область стека.
EDIT Чтобы снова было абсолютно ясно (если это не было уже ясно из приведенного ниже примера), дело не в том, что ref
/ out
может только указывать на стек. Дело в том, что , когда указывает на стек, правила языка гарантируют, что он не станет висящим указателем. Эта гарантия необходима (и здесь актуальна / интересна), потому что стек просто отбрасывает информацию в соответствии с выходами из вызова метода, без каких-либо проверок, чтобы гарантировать, что какие-либо источники ссылок все еще существуют.
И наоборот, когда ref
/ ] out
относится к объектам в куче сборщика мусора it ' Неудивительно, что эти объекты могут оставаться в живых столько, сколько необходимо: куча сборщика мусора предназначена именно для хранения объектов в течение любого периода времени, необходимого для их источников перехода, и обеспечивает закрепление (см. пример ниже) для поддержки ситуаций. где объект не должен перемещаться с помощью сжатия GC.
Если вы когда-нибудь играли с взаимодействием в небезопасном коде, вы обнаружите, что ref
очень тесно связан с указателями. Например, если COM-интерфейс объявлен так:
HRESULT Write(BYTE *pBuffer, UINT size);
Сборка взаимодействия превратит его в это:
void Write(ref byte pBuffer, uint size);
И вы можете сделать это, чтобы вызвать его (я считаю, что компонент COM-взаимодействия позаботится о закреплении массива):
byte[] b = new byte[1000];
obj.Write(ref b[0], b.Length);
Другими словами, ref
к первому байту дает вам доступ ко всему этому; очевидно, это указатель на первый байт.
Если вы когда-либо поиграйте с взаимодействием в небезопасном коде, вы обнаружите, что ref
очень тесно связан с указателями. Например, если COM-интерфейс объявлен так:
HRESULT Write(BYTE *pBuffer, UINT size);
Сборка взаимодействия превратит его в это:
void Write(ref byte pBuffer, uint size);
И вы можете сделать это, чтобы вызвать его (я считаю, что компонент COM-взаимодействия позаботится о закреплении массива):
byte[] b = new byte[1000];
obj.Write(ref b[0], b.Length);
Другими словами, ref
к первому байту дает вам доступ ко всему этому; очевидно, это указатель на первый байт.
Если вы когда-либо поиграйте с взаимодействием в небезопасном коде, вы обнаружите, что ref
очень тесно связан с указателями. Например, если COM-интерфейс объявлен так:
HRESULT Write(BYTE *pBuffer, UINT size);
Сборка взаимодействия превратит его в это:
void Write(ref byte pBuffer, uint size);
И вы можете сделать это, чтобы вызвать его (я считаю, что компонент COM-взаимодействия позаботится о закреплении массива):
byte[] b = new byte[1000];
obj.Write(ref b[0], b.Length);
Другими словами, ref
к первому байту дает вам доступ ко всему этому; очевидно, это указатель на первый байт.
Если вы когда-нибудь играли с взаимодействием в небезопасном коде, вы обнаружите, что ref
очень тесно связан указателям. Например, если COM-интерфейс объявлен так:
HRESULT Write(BYTE *pBuffer, UINT size);
Сборка взаимодействия превратит его в это:
void Write(ref byte pBuffer, uint size);
И вы можете сделать это, чтобы вызвать его (я считаю, что компонент COM-взаимодействия позаботится о закреплении массива):
byte[] b = new byte[1000];
obj.Write(ref b[0], b.Length);
Другими словами, ref
к первому байту дает вам доступ ко всему этому; очевидно, это указатель на первый байт.
Если вы когда-нибудь играли с взаимодействием в небезопасном коде, вы обнаружите, что ref
очень тесно связан указателям. Например, если COM-интерфейс объявлен так:
HRESULT Write(BYTE *pBuffer, UINT size);
Сборка взаимодействия превратит его в это:
void Write(ref byte pBuffer, uint size);
И вы можете сделать это, чтобы вызвать его (я считаю, что компонент COM-взаимодействия позаботится о закреплении массива):
byte[] b = new byte[1000];
obj.Write(ref b[0], b.Length);
Другими словами, ref
к первому байту дает вам доступ ко всему этому; очевидно, это указатель на первый байт.
HRESULT Write(BYTE *pBuffer, UINT size);
Сборка взаимодействия преобразует его в это:
void Write(ref byte pBuffer, uint size);
И вы можете сделать это, чтобы вызвать его (я считаю, что компонент COM-взаимодействия позаботится о закреплении массива):
byte[] b = new byte[1000];
obj.Write(ref b[0], b.Length);
Другими словами, ref
к первому байту дает вам доступ ко всему этому; очевидно, это указатель на первый байт.
HRESULT Write(BYTE *pBuffer, UINT size);
Сборка взаимодействия преобразует его в это:
void Write(ref byte pBuffer, uint size);
И вы можете сделать это, чтобы вызвать его (я считаю, что компонент COM-взаимодействия позаботится о закреплении массива):
byte[] b = new byte[1000];
obj.Write(ref b[0], b.Length);
Другими словами, ref
к первому байту дает вам доступ ко всему этому; очевидно, это указатель на первый байт.
Справочные параметры в C # могут использоваться для замены одного использования указателей, да. Но не все.
Другое распространенное использование указателей - это средство для итерации по массиву. Параметры Out / ref не могут этого сделать, поэтому нет, они не «такие же, как указатели».
На самом деле, я бы сравнил их со ссылками на C ++, а не с указателями. Указатели в C ++ и C являются более общей концепцией, и ссылки будут делать то, что вы хотите.
Все это, несомненно, указатели под крышками, конечно.
ref
и out
используются только с аргументами функции, чтобы указать, что аргумент должен передаваться по ссылке вместо значения. В этом смысле да, они чем-то похожи на указатели в C ++ (на самом деле больше похожи на ссылки). Подробнее об этом читайте в этой статье .
Хорошая вещь в использовании out заключается в том, что вам гарантировано, что элементу будет присвоено значение - в противном случае вы получите ошибку компиляции.
Короткий ответ - да (аналогичная функциональность, но не совсем тот же механизм).
В качестве примечания: если вы используете FxCop для анализа кода, использование out
и ref
приведет к ошибке «Microsoft.Design» «CA1045: DoNotPassTypesByReference.»
В то время как сравнения находятся в поле зрения смотрящего ... Я говорю нет. 'ref' изменяет соглашение о вызовах , но не тип параметров. В вашем примере на C ++ d1 и d2 имеют тип int *. В C # они все еще являются Int32, они просто передаются по ссылке, а не по значению.
Между прочим, ваш код на C ++ этого не делает. На самом деле меняют местами входы в традиционном смысле. Обобщая это так:
template<typename T>
void swap(T *d1, T *d2)
{
T temp = *d1;
*d1 = *d2;
*d2 = temp;
}
... не будет работать, если все типы T не будут иметь конструкторы копирования, и даже в этом случае будет гораздо более неэффективно, чем замена указателей.