C# ref в сравнении с out

Ключевые слова Ref и out в C# используются для передачи аргументов внутри метода или функции. Оба слова указывают на то, что аргумент/параметр передается по ссылке. По умолчанию параметры передаются в метод по значению. Используя эти ключевые слова (ref и out), мы можем передать параметр по ссылке.

Ключевое слово ref

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

Пример кода

public static string GetNextName(ref int id)
{
    string returnText = "Next-" + id.ToString();
    id += 1;
    return returnText;
}
static void Main(string[] args)
{
    int i = 1;
    Console.WriteLine("Previous value of integer i:" + i.ToString());
    string test = GetNextName(ref i);
    Console.WriteLine("Current value of integer i:" + i.ToString());
}

Вывод

Ключевое слово out

Ключевое слово out передает аргументы по ссылке. Это очень похоже на ключевое слово ref.

Пример кода

public static string GetNextNameByOut(out int id)
{
    id = 1;
    string returnText = "Next-" + id.ToString();
    return returnText;
}
static void Main(string[] args)
{
    int i = 0;
    Console.WriteLine("Previous value of integer i:" + i.ToString());
    string test = GetNextNameByOut(out i);
    Console.WriteLine("Current value of integer i:" + i.ToString());
}

Вывод

Ref в сравнении с Out

Ref

Out

Параметр или аргумент должен быть сначала инициализирован, прежде чем он будет передан в ref.

Инициализация параметра или аргумента перед передачей его в out не является обязательной.

Не требуется присваивать или инициализировать значение параметра (который передается по ref) перед возвратом в вызывающий метод.

Вызываемый метод обязан присвоить или инициализировать значение параметра (который передается в out) перед возвратом в вызывающий метод.

Передача значения параметра по Ref полезна, когда вызываемый метод также должен модифицировать передаваемый параметр.

Объявление параметра в методе out полезно, когда из функции или метода необходимо вернуть несколько значений.

Инициализация значения параметра перед его использованием в вызывающем методе не обязательна.

Значение параметра должно быть инициализировано в вызывающем методе перед его использованием.

Когда мы используем REF, данные могут передаваться двунаправленно.

Когда мы используем OUT, данные передаются только однонаправленно (от вызываемого метода к вызывающему методу).

И ref, и out по-разному обрабатываются во время выполнения программы, а во время компиляции они обрабатываются одинаково.

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

Ключевое слово Ref / Out и перегрузка методов

И ref, и out обрабатываются по-разному во время выполнения программы, и одинаково во время компиляции, поэтому методы не могут быть перегружены, если один метод принимает аргумент как ref, а другой — как out.

Пример кода

public static string GetNextName(ref int id)
{
    string returnText = "Next-" + id.ToString();
    id += 1;
    return returnText;
}
public static string GetNextName(out int id)
{
    id = 1;
    string returnText = "Next-" + id.ToString();
    return returnText;
}

Вывод при компиляции кода:

Однако перегрузка методов возможна, когда один метод принимает аргумент ref или out, а другой принимает тот же аргумент без ref или out.

Пример кода

public static string GetNextName(int id)
{
    string returnText = "Next-" + id.ToString();
    id += 1;
    return returnText;
}
public static string GetNextName(ref int id)
{
    string returnText = "Next-" + id.ToString();
    id += 1;
    return returnText;
}

Резюме

Ключевые слова out и ref полезны, когда мы хотим вернуть значение в тех же переменных, которые были переданы в качестве аргумента.


Материал подготовлен в рамках специализации «C# Developer». Если вам интересно узнать подробнее о формате обучения и программе, познакомиться с преподавателем курса — приглашаем на день открытых дверей онлайн. Регистрация здесь.

Комментарии (12)


  1. DistortNeo
    27.09.2021 20:23
    +4

    И ref, и out по-разному обрабатываются во время выполнения программы, а во время компиляции они обрабатываются одинаково.

    И во время выполнения программы они тоже обрабатываются одинаково. Кстати, есть забавный хак: с помощью System.Runtime.CompilerServices.Unsafe.SkipInit<T> можно обойти требование об обязательной инициализации out-параметра в вызываемом методе.


    1. IvanG
      28.09.2021 12:51

      Автор перепутал, во время компиляции они по разному обрабатываются, в out проверка на присвоение в методе есть, в ref - нет


  1. shai_hulud
    27.09.2021 21:00
    +4

    Вся прелесть ref в C# раскрывается в работе с unmanaged code:

    	public unsafe struct uv_timer_t
    	{
        // для ref можно делать extenstion методы
    		public static void Stop(this ref uv_timer_t timerHandleRef)
    		{
    				var handlePtr = (IntPtr)Unsafe.AsPointer(ref handle);
    		}
        
    		[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
    		private static extern int uv_timer_stop(IntPtr handle);
    	}


    И далее в коде получаете поинтер на uv_timer_t в unmanaged памяти, преобразуете его в ref uv_timer_t и работаете уже почти как с настоящим типизированным объектом, вызываете методы, передаете дальше по стеку итд. Хранить нельзя, можно преобразовать обратно в IntPtr и хранить его.

    ref var timerRef = ref Unsafe.AsRef<uv_timer_t>((void*)POINTER_TO_UNMANAGED_MEMORY);
    
    timerRef.Stop(); // extension method на ref переменных работают by ref
    
    myFunc(ref timerRef); // можно передавать в методы, разыменовывать можно, но не нужно :)


    1. DistortNeo
      28.09.2021 00:20
      +1

      Хранить нельзя

      А чтобы не было соблазна так делать, объявите типа как ref struct.


  1. Mikluho
    28.09.2021 00:41
    +3

    Чую, выпускники вашего курса будут заваливать интервью даже на позицию junior...

    Это ж надо, так запутать простые объяснения...

    По умолчанию параметры передаются в метод по значению.

    Это справедливо для value-типов

    Ключевое слово out передает аргументы по ссылке. Это очень похоже на ключевое слово ref.

    Это как так-то? А в чём разница? (да, там потом есть табличка, но вот прямо тут лажа).

    Требования про инициализацию параметров раскрыта неверно. И про двунаправленную передачу тоже фигня написана...

    Тема про ref и out для reference типов не раскрыта.


    1. ad1Dima
      28.09.2021 02:25
      -3

      Чую, выпускники вашего курса будут заваливать интервью даже на позицию junior...

      Или нет


      1. Politura
        28.09.2021 04:03
        +5

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

        Хочется плюсиков за переводы - переводите статьи нормальных чуваков, а не хрен пойми кого, например по C# вот: https://blog.marcgravell.com/


        1. ad1Dima
          28.09.2021 08:01
          -1

          будут заваливать интервью даже на позицию junior...

          чтоб тыкая в свой профиль наняться на позицию пожирнее

          Собственно о том и речь, пройдут и наймутся )


    1. KGeist
      28.09.2021 07:08
      +3

      >Это справедливо для value-типов

      По умолчанию, ссылки на reference-объекты передаются по значению. Явно это имелось в виду. Можно передавать значение ссылки по ссылке, если использовать ref или out.


      1. Mikluho
        28.09.2021 08:20

        Оно как бы да, так и есть, но не многие понимают, что это значит и как работает.

        А потом удивляются, что написанное в методе 'someStringVar = "42"; ' не работает.


    1. DistortNeo
      28.09.2021 14:01
      +1

      А можно ещё больше всех запутать и вспомнить про in.
      Так-то и in, и ref, и out — это абсолютно одно и то же, но с разными ограничениями, накладываемым при компиляции.


  1. moroz69off
    01.10.2021 07:39

    Кроме информационной каши в тексте статьи есть ещё неприятность -- оформление. В таблице ключевое слово языка ref переживает три начертания: ref, Ref, REF. Куда это годится?