Я только что сделал подпрограмму Swap на C # вот так:

static void Swap(ref int x, ref int y)
{
    int temp = x;
    x = y;
    y = temp;
}

Он делает то же самое, что и этот код C ++:

void swap(int *d1, int *d2)
{
    int temp=*d1;
    *d1=*d2;
    *d2=temp;
}

Так являются ли ключевые слова ref и out указателями для C # без использования кода unsafe?

8
Bob Dylan 22 Июл 2009 в 22:46

7 ответов

Лучший ответ

Они более ограничены. Вы можете сказать ++ на указателе, но не на ref или out.


РЕДАКТИРОВАТЬ . В комментариях есть некоторая путаница, поэтому для полной ясности: здесь нужно сравнить возможности указателей. Вы не можете выполнить ту же операцию, что и ptr++ с ref / out, то есть сделать так, чтобы он адресовал соседнее место в памяти. Это правда (но здесь не имеет значения), что вы можете выполнить эквивалент (*ptr)++, но это было бы для сравнения его с возможностями значений , а не указателей.


Можно с уверенностью сказать, что они внутри являются просто указателями, потому что стек не перемещается, а C # тщательно организован так, чтобы ref и out всегда ссылались на активную область стека.


РЕДАКТИРОВАТЬ Чтобы еще раз прояснить (если это еще не было ясно из приведенного ниже примера), дело здесь не в том, что ref / out может только < / em> указать на стек. Дело в том, что когда он указывает на стек, правила языка гарантируют, что он не станет висящим указателем. Эта гарантия необходима (и здесь актуальна / интересна), потому что стек просто отбрасывает информацию в соответствии с выходами из вызова метода, без каких-либо проверок, чтобы убедиться, что какие-либо рефереры все еще существуют.

И наоборот, когда ref / out ссылается на объекты в куче сборщика мусора, неудивительно, что эти объекты могут оставаться в живых столько, сколько необходимо: куча сборщика мусора предназначена именно для того, чтобы сохранять объекты для любой промежуток времени, необходимый для их рефереров, и обеспечивает закрепление (см. пример ниже) для поддержки ситуаций, когда объект не должен перемещаться путем сжатия 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 в первом байте дает вам доступ ко всему этому; очевидно, это указатель на первый байт.

10
Daniel Earwicker 21 Янв 2015 в 08:28

Справочные параметры в C # можно использовать для замены одного использования указателей, да. Но не все.

Еще одно распространенное использование указателей - это средство для итерации по массиву. Параметры out / ref не могут этого сделать, поэтому нет, они не «такие же, как указатели».

6
jalf 22 Июл 2009 в 18:50

ref и out используются только с аргументами функции, чтобы указать, что аргумент должен передаваться по ссылке, а не по значению. В этом смысле да, они чем-то похожи на указатели в C ++ (на самом деле больше похожи на ссылки). Подробнее об этом читайте в этой статье.

3
Saulius Valatka 22 Июл 2009 в 18:54

Преимущество использования out заключается в том, что вам гарантировано, что элементу будет присвоено значение - в противном случае вы получите ошибку компиляции.

1
bufferz 22 Июл 2009 в 18:53

Хотя сравнения в глазах смотрящего ... Я говорю нет. '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 не имеют конструкторов копирования, и даже в этом случае это будет гораздо более неэффективно, чем замена указателей.

1
Richard Berg 22 Июл 2009 в 18:58

Короткий ответ - да (аналогичная функциональность, но не совсем тот же механизм). В качестве примечания: если вы используете FxCop для анализа своего кода, использование out и ref приведет к ошибке «Microsoft.Design» «CA1045: DoNotPassTypesByReference».

1
Erich Mirabal 22 Июл 2009 в 19:09

Собственно, я бы сравнил их со ссылками на C ++, а не с указателями. Указатели в C ++ и C представляют собой более общую концепцию, и ссылки будут делать то, что вы хотите.

Все это, несомненно, указатели под прикрытием.

2
David Thornley 22 Июл 2009 в 18:54