Вопросы о параметрах Ref и Out

Я думал, что понял разницу, но теперь я не так уверен. Я читал технический ответ несколько раз, но не понимаю, что происходит. У меня есть такой пример.

class Program
   {
      static void Main()
      {
         int val = 0;

         Example1(val);
         Console.WriteLine(val); // Still 0!

         Example2(ref val);
         Console.WriteLine(val); // Now 2!

         Example3(out val);
         Console.WriteLine(val); // Now 3!
      }

      static void Example1(int value)
         {
             value = 1;
         }

      static void Example2(ref int value)
         {
             value = 2;
         }

      static void Example3(out int value)
         {
           value = 3;
         }
    }

Я всегда думал, что разница между параметрами по умолчанию заключается в том, что если я передаю val в Example1, я не могу использовать присваивание.

Но с ключевым словом ref значение val по-прежнему равно 0, но я создал ссылку, которая теперь обрабатывается как переменная "значение" в Example2 (ref val). Я пока что на чем-то зациклился? если бы я использовал

int value = 0;
Example1(value);
Console.WriteLine(value); // this would then return 1 correct?

Итак, что происходит с ключевым словом out? То же, что и ref?


person christopher clark    schedule 24.05.2015    source источник
comment
Что значит параметры по умолчанию? Вы имеете в виду, когда ref или out не используется?   -  person John Saunders    schedule 25.05.2015
comment
@JohnSaunders Да, когда ref и out не используются. Хотелось бы, чтобы это был дубликат, я читал этот пост несколько раз. Я все еще не понимаю.   -  person christopher clark    schedule 25.05.2015
comment
Ваш последний блок кода Если бы я использовал ... именно то, что вы делаете в первых 3 строках вашей Main функции в первом блоке кода.   -  person Blorgbeard    schedule 25.05.2015
comment
Я думаю, что это дубликат, и вы его просто не понимаете. Что не получается? Обратите внимание, что между передачей типа значения (например, int) и передачей ссылочного типа (например, экземпляра класса) вещи немного отличаются.   -  person John Saunders    schedule 25.05.2015
comment
Кстати, пожалуйста, прекратите использовать параметры по умолчанию. Это слишком близко к использованию C # public void Method(int val = 1), где вам не нужно передавать val, если вы хотите передать 1.   -  person John Saunders    schedule 25.05.2015


Ответы (3)


Посмотрите, поможет ли это:

Никаких "декораторов"

static void Example(int value)
{
    Console.WriteLine("Value is {0}", value);
    value = 99;
}

usage
{
    int value = 10;
    Example(notADefaultValue); // Will print <Value is 10>
    Console.WriteLine("Value is {0}", value); // will print <Value is 10>
}

Сводка: типы значений (структуры / перечисления / int / double / и т. Д.) По умолчанию не передаются в качестве ссылки, поэтому все, что вы делаете с переменной внутри метода, не влияет на вызывающего. Таким образом, ключевое слово "ref". Если вы передадите ссылочный тип методу (т. Е. Класс) и измените его внутренние компоненты, это повлияет на вызывающего.

Параметры по умолчанию:

static void Example(int value = 5)
{
    Console.WriteLine("Value is {0}", value);
}

usage
{
    int notADefaultValue = 10;
    Example(notADefaultValue); // Will print <Value is 10>
    Example(); // will print <Value is 5>
}

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

Параметры ссылки:

static void Example(ref int value)
{
    Console.WriteLine("Value is {0}", value);
    value = 99;
}

usage
{
    int value = 10;
    Example(ref value); // Will print <Value is 10>
    Console.WriteLine("Value is {0}", value); // Will print <Value is 99>
}

Резюме: Если вы передадите тип значения в качестве ссылки (ссылки), само значение будет изменено. По умолчанию все ссылочные типы передаются как ref. К вашему сведению, int - это примитивные типы, поэтому требуется явная ссылка.

параметры выхода:

static void Example(out int value)
{
    value = 99;
    Console.WriteLine("Value is {0}", value);
}

usage
{
    int value; // no need to init it
    Example(out value); // Will print <Value is 99>
    Console.WriteLine("Value is {0}", value); // Will print <Value is 99>
}

Резюме: параметры вывода похожи на возвращаемые переменные, но передаются через сигнатуру метода. Наиболее распространенным примером является TryParse, где метод возвращает важную информацию, и в зависимости от этой информации параметр out является действительным или нет (действительным, если он истинен).

person Ruskin    schedule 25.05.2015
comment
OP неправильно использовал параметры термина по умолчанию. На самом деле он просто имел в виду параметры, которые не используют ни ref, ни out. - person John Saunders; 25.05.2015
comment
Ничего страшного в объяснении значений по умолчанию. Я отредактировал ответ, включив в него параметры, которые не используют ни ref, ни out - person Ruskin; 25.05.2015
comment
Прекрасно, я бы не понял этого без вас, @JohnSaunders. - person christopher clark; 25.05.2015

В первом примере вы не указываете, что переменная является ссылкой, и, поскольку это основной тип, она просто копирует предоставленный номер. Итак, value внутри Example1 - это копия value в Main.

Во втором примере используется ссылка. Это означает, что как внутри функции Example2, так и внутри Main ссылаются на одно и то же место в памяти, и поэтому значение передается при входе и выходе из функции.

В третьем примере ключевое слово out делает то же самое, что и второе, за исключением того, что оно инициализируется как 0 при вводе в функцию. В результате это просто параметр для возврата каких-то данных. Значение просто передается при выходе из функции.

person Highace2    schedule 24.05.2015
comment
Вы путаете ref со ссылочными типами. - person John Saunders; 25.05.2015
comment
@JohnSaunders Фундаментальные типы по умолчанию передаются по значению, а все остальные по ссылке. ref - это просто способ явно передать его по ссылке. - person Highace2; 25.05.2015
comment
Нет, ты снова ошибаешься. Различаются типы значений и ссылочные типы, а ref не похож на ByRef в Visual Basic. Это не означает передачу по ссылке. - person John Saunders; 25.05.2015
comment
@JohnSaunders Нет, проверьте документацию msdn.microsoft.com/en-us/ библиотека / 14akc2c7.aspx - person Highace2; 25.05.2015
comment
Нет, см. Типы (Руководство по программированию C #). - person John Saunders; 25.05.2015
comment
Не путайте концепцию передачи по ссылке с концепцией ссылочных типов. Эти две концепции не совпадают. Параметр метода можно изменить с помощью ref независимо от того, является ли он типом значения или ссылочным типом. При передаче по ссылке отсутствует упаковка типа значения. - person John Saunders; 25.05.2015
comment
Передача по ссылке не означает, что переменная является ссылкой. - person John Saunders; 25.05.2015
comment
Согласны ли вы, что переменная внутри вызываемой функции относится к тому же номеру, что и вне функции, независимо от того, какая из двух была изменена? Тогда они каким-то образом относятся к одному и тому же месту в памяти. Конечно, за кадром, но все же ссылка. - person Highace2; 25.05.2015
comment
Нет, я согласен с тем, что изменения, внесенные в формальный параметр в методе, отражаются в фактическом параметре вызывающей стороны. Механизм может включать или не включать ссылки. Обратите внимание на сходство с out, где вы фактически не можете получить доступ к какому-либо значению фактического параметра, находясь внутри метода. ref также можно выполнить, скопировав исходящий метод при вызове, а out можно выполнить вызовом метода-копирования. - person John Saunders; 25.05.2015
comment
void Do (MyClass p) {p = новый MyClass (); } vs void Do (ссылка MyClass p) {p = new MyClass (); } Просто протестируйте эти два метода, и вы поймете разницу. Вы можете изменить что-то внутри 'p' без разницы в поведении, но вы не можете назначить новую ссылку в обоих случаях. - person Malmi; 25.05.2015
comment
@ Highace2 - Джон прав. Существует большая разница. Когда вы передаете ссылочную переменную с помощью ref, это то же самое, что и двойной указатель (**) в C. Если вы передаете ссылочную переменную без ref, это похоже на одиночный указатель (*). - person Enigmativity; 25.05.2015

Хорошо, это дубликат связанного вопроса в комментариях, но я постараюсь вам это объяснить.

Неукрашенные параметры

Случай A: public void MyFunc(int x) { }

-or-

Случай B: public void MyFunc(MyClass y) { }

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

В случае B параметр является ссылочным типом. По умолчанию эти типы передаются как есть. Они не копируются, но вы не можете изменить ссылку (присвоив типу значение new или null). Вы можете изменить содержимое объекта (любое его свойство / поле), и оно будет отражено обратно в месте вызова.

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

Случай A: public void MyFunc(ref int x) { }

-or-

Случай B: public void MyFunc(ref MyClass x) { }

В случае A вы сообщаете компилятору, что хотите передать значение по ссылке, что означает, что компилятор не копирует тип, а передает ссылку на этот тип. Функция может изменить это.

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

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

Случай A: public void MyFunc(out int x) { }

-or-

Случай B: public void MyFunc(out MyClass x) { }

Здесь вы в основном определяете дополнительные типы возврата для функции. Это параметр метода, указывающий вызывающей стороне ожидать результата вместо переменной x. То же самое для обоих. В любом случае вызывающему абоненту не следует ожидать, что любое значение, которое было до x, впоследствии будет таким же. Фактически, вы можете ожидать, что оно не будет таким же, потому что метод требуется для присвоения нового значения x, прежде чем он сможет вернуться.

out в основном означает, что вы предоставляете место для возвращаемого значения, для типов значений просто используйте конструктор по умолчанию, а для ссылочных типов инициализируйте значение null перед его передачей.

person Ron Beyer    schedule 25.05.2015