Используя КАСАТЕЛЬНО и ключевые слова с Передачей Ссылкой и Передачей Значением в C#

Я использовал Bakefile прежде с некоторым успехом. Это довольно просто и, кажется, работает хорошо.

8
задан Dave Swersky 5 November 2009 в 20:36
поделиться

7 ответов

Вы бы никогда никогда не объединяли 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
}
18
ответ дан 5 December 2019 в 05:56
поделиться

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

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

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

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

Изменить:

1
ответ дан 5 December 2019 в 05:56
поделиться

Помощь с благодарностью

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

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

Да, это точно.

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

Не совсем. Начните с тщательного различения значений и переменных . Рассмотрим:

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, это не меняет blah; foo содержит копию значения blah.

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

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

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

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

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

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

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

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

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

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

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

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

REF - это два способа, как внутри, так и снаружи.

Верно.

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

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

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

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

Опять же, "

8
ответ дан 5 December 2019 в 05:56
поделиться

Изменить: Я исправил этот ответ, чтобы отразить, что ключевое слово 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. Единственная разница в том, как компилятор обрабатывает их для определенных целей назначения.

1
ответ дан 5 December 2019 в 05:56
поделиться

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

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

0
ответ дан 5 December 2019 в 05:56
поделиться

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

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

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

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

EDIT: пример:

void mymethod2(out string a) {
  a="hello";
}
0
ответ дан 5 December 2019 в 05:56
поделиться

Если вы разбираетесь в C ++, возможно, это поможет вам:

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)
0
ответ дан 5 December 2019 в 05:56
поделиться
Другие вопросы по тегам:

Похожие вопросы: