Использование ключевых слов REF и OUT с передачей по ссылке и передачей по значению в C#

StackOverflow https://stackoverflow.com/questions/1683394

  •  18-09-2019
  •  | 
  •  

Вопрос

Вот что я понимаю на данный момент:

ПЕРЕДАЧА ПО ЗНАЧЕНИЮ

Передача по значению означает, что передается копия аргумента.Изменения этой копии не меняют оригинал.

ПЕРЕХОД ПО ССЫЛКЕ

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

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

REF сообщает компилятору, что объект инициализируется перед входом в функцию.REF означает, что значение уже установлено, поэтому метод может его прочитать и изменить.REF — это два пути: внутрь и наружу.

ВЫХОД Ключевое слово

OUT сообщает компилятору, что объект будет инициализирован внутри функции.OUT означает, что значение еще не установлено и, следовательно, должно быть установлено перед вызовом return.OUT — это только один путь — out.

Вопрос

Итак, в каких сценариях вы бы объединили использование ключевых слов ref и out с передачей по ссылке или передачей по значению?Примеры очень помогли бы.

Помощь очень ценится.

Это было полезно?

Решение

Ты бы никогда объединить ref и out по 1 параметру.Оба они означают «передавать по ссылке».

Конечно, вы можете объединить параметры ref и параметры out в одном методе.

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

Но помимо намерения, компилятор C# отслеживает определенное присвоение, и это имеет наиболее заметное отличие.Это также предотвращает неправильное использование (чтение) выходного параметра.

void SetOne(out int x) 
{
  int y = x + 1; // error, 'x' not definitely assigned.
  x = 1;         // mandatory to assign something
}

void AddTwo(ref int x)
{
    x = x + 2;  // OK, x  is known to be assigned
}

void Main()
{
    int foo, bar;

    SetOne(out foo); // OK, foo does not have to be assigned
    AddTwo(ref foo); // OK, foo assigned by SetOne
    AddTwo(ref bar); // error, bar is unassigned
}

Другие советы

Помощь очень ценится

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

Передача по значению означает, что передается копия аргумента.

Да, это совершенно точно.

Изменения этой копии не меняют оригинал.

Не совсем.Начните с тщательного различения между ценности и переменные.Учитывать:

class Foo { public int x; }
...
void N() 
{
  Foo blah = new Foo();
  blah.x = 0;
  M(blah);
}
...
void M(Foo foo)
{
  foo.x = 123; // changes blah.x
  foo = null; // does not change blah
}

Переменные здесь — x, blah и foo.x — поле, blah — локальное, foo — формальный параметр.

Значения здесь — null, 0, 123 и ссылка на экземпляр Foo. Эта ссылка является значением. Крайне важно понять этот факт.

Копия значения blah передается путем копирования значения переменной blah в переменную foo.Значение blah — это ссылка на экземпляр Foo.

M может изменить значение переменной x, поскольку у M есть копия значения blah, которое является ссылкой на Foo.Когда M меняет содержимое foo на null, это не меняет ничего;foo содержит копию значения blah.

Передача по ссылке означает, что передается ссылка на оригинал.

Тщательно выбирайте формулировку.Что такое «оригинал»?

Лучше было бы сказать, что «передача по ссылке означает, что передается ссылка на аргумент, который должен быть переменной».Более простой способ подумать об этом состоит в том, что «передача по ссылке делает параметр псевдонимом для переменной, переданной в качестве аргумента».

изменения в ссылке влияют на оригинал.

Поскольку параметр и аргумент являются псевдонимами друг друга, изменения в ссылке не влияют на оригинал;ссылка ЯВЛЯЕТСЯ оригиналом.Они оба та же переменная.

REF сообщает компилятору, что объект инициализируется перед входом в функцию.

«Объект» бессмысленен.Вы имеете в виду "переменную".

«ref» не «сообщает компилятору, что переменная инициализирована».Скорее, «ref» сообщает компилятору: «Вы, компилятор, должны убедиться, что переменная инициализирована».Это совсем другое!

REF означает, что значение уже установлено,

Нет, ссылка требует, чтобы переменная уже установлен.Нет такого понятия, как «установка значения».

поэтому метод может прочитать его и изменить.

Где под «этим» вы подразумеваете «переменную».

REF — это два пути: внутрь и наружу.

Правильный.

OUT сообщает компилятору, что объект будет инициализирован внутри функции.

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

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

OUT означает, что значение еще не установлено,

Опять же, под «значением» вы подразумеваете «переменную».Но это не точно.Совершенно законно передавать инициализированную переменную в качестве «выходного» параметра.Бессмысленно, но законно.

и поэтому должен быть установлен перед вызовом return.

«возврат» не вызывается;методы называются.Но да, метод должен присвоить значение переменной перед обычным возвратом.

OUT — это только один путь — out.

Верно.

Итак, в каких сценариях вы бы объединили использование ключевых слов ref и out?

Таких сценариев не бывает.

Вы понимаете динамику прохождения в любом случае.Некоторые сценарии параметров могут быть следующими:

  • ref int num для входного/выходного параметра.Функция может измените значение в нем.
  • out int num как и ref, кроме функции, которая должна присвоить ей значение перед возвратом.

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

Иногда поставщики доступа к данным, например некоторые методы ADO.NET, используют выходные параметры для обратной доставки информации.Некоторые методы доступа к данным имитируют хранимые процедуры базы данных, которые имеют параметры ввода/вывода.

Редактировать: Одним из условий для ссылочных типов являются параметры ref StringBuilder word или StringBuilder word (по значению) ведут себя одинаково - это влияет на внешнюю строку, хотя базовая реализация может немного отличаться, поскольку в этот момент фокусом является ссылка, а не значение в куче.

Редактировать: Я исправил этот ответ, чтобы отразить, что ключевое слово out в C# не делает того, что вы могли бы от него ожидать (например, настоящий параметр OUT в смысле этого термина в информатике).Первоначально я заявил, что «out» передается по значению OUT, но оказался неверным.

Вы не можете использовать «out» и «ref» вместе.В C# (NET Framework) существует три соглашения о вызовах:

  • Нет ключевого слова = передача по значению (IN)
  • Ключевое слово 'out' = передача по ссылке (REF) без требования определенного назначения перед вызовом
  • Ключевое слово 'ref' = передача по ссылке (REF) с определенным требованием назначения перед вызовом.

В C# нет настоящих параметров OUT или IN-OUT.

Чтобы убедиться, что параметры out в C# не являются истинными параметрами OUT, вы можете использовать следующий код:

  public class Test
  {
    Action _showValue;

    public void Run()
    {
      string local = "Initial";
      _showValue = () => { Console.WriteLine(local.ToString()); };

      Console.WriteLine("Passing by value");
      inMethod(local);

      Console.WriteLine("Passing by reference with 'out' keyword");
      outMethod(out local);

      Console.WriteLine("Passing by reference with 'ref' keyword");
      refMethod(ref local);

    }

    void inMethod(string arg)
    {
      _showValue();
      arg = "IN";
      _showValue();
    }

    void outMethod(out string arg)
    {
      _showValue();
      arg = "OUT";
      _showValue();
    }

    void refMethod(ref string arg)
    {
      _showValue();
      arg = "REF";
      _showValue();
    }
  }

Результат:

Passing by value
Initial
Initial
Passing by reference with 'out' keyword
Initial
OUT
Passing by reference with 'ref' keyword
OUT
REF

Как видите, и «out», и «ref» фактически проходят через REF.Единственная разница заключается в том, как компилятор обрабатывает их для определенных целей присваивания.

Используя OUT Ключевое слово полезно, если у вас есть метод, который должен возвращать более одного значения.Например, посмотрите на такие методы, как int.TryParse().

С использованием REF больше для ясности объектов.Учитывая, что любой непримитив, передаваемый в метод, по своей сути передается по ссылке, в обычном управляемом коде в этом нет особой необходимости.Объявляя ключевое слово REF, вы заявляете, что параметр, скорее всего, будет изменен в теле метода, и, следовательно, вызывающий код должен знать об этом (поэтому вам нужно явно добавить ref в вызывающем коде также.

Если вы понимаете С++, возможно, это вам поможет:

void Foo(Bar) {} // pass by value c# (if it's a value type ;))
void Foo(Bar) {} // c++

void Foo(Bar) {} // pass by reference c# (if it's a reference type ;))
void Foo(Bar&) {} // c++

void Foo(ref Bar) {} // c#
void Foo(Bar*) // c++

void Foo(out Bar) {} // c#
void Foo(Bar**) {} // c++ (the contents of *Bar needs to be filled up)

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

РЕДАКТИРОВАТЬ:пример:

void mymethod(ref int a) {
  a++;
}

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

РЕДАКТИРОВАТЬ:пример:

void mymethod2(out string a) {
  a="hello";
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top