В чем разница между ключевыми словами ref и out? - PullRequest
821 голосов
/ 23 декабря 2008

Я создаю функцию, в которой мне нужно передать объект, чтобы он мог быть изменен функцией. В чем разница между:

public void myFunction(ref MyClass someClass)

и

public void myFunction(out MyClass someClass)

Что мне использовать и почему?

Ответы [ 23 ]

6 голосов
/ 23 декабря 2008

"Бейкер"

Это потому, что первая меняет вашу строковую ссылку, чтобы указать на "Бейкер". Изменение ссылки возможно, потому что вы передали ее через ключевое слово ref (=> ссылка на ссылку на строку). Второй вызов получает копию ссылки на строку.

Поначалу строка

выглядит какой-то особенной. Но string это просто ссылочный класс, и если вы определите

string s = "Able";

then s - ссылка на строковый класс, содержащий текст «Able»! Еще одно присвоение той же переменной через

s = "Baker";

не изменяет исходную строку, а просто создает новый экземпляр и позволяет указывать на этот экземпляр!

Вы можете попробовать это на следующем небольшом примере кода:

string s = "Able";
string s2 = s;
s = "Baker";
Console.WriteLine(s2);

Чего вы ожидаете? То, что вы получите, все еще «Able», потому что вы просто устанавливаете ссылку в s на другой экземпляр, в то время как s2 указывает на исходный экземпляр.

EDIT: Строка также является неизменной, что означает, что просто нет метода или свойства, которое изменяет существующий экземпляр строки (вы можете попытаться найти его в документе, но вы не найдете ничего :-)). Все методы обработки строк возвращают новый экземпляр строки! (Вот почему вы часто получаете лучшую производительность при использовании класса StringBuilder)

5 голосов
/ 06 декабря 2014

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


out сообщает компилятору, что инициализация объекта является ответственностью функция, которую функция должна назначить выходному параметру. Не разрешается оставлять его без назначения.

4 голосов
/ 25 октября 2014

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

Следующий пример иллюстрирует это:

using System;

namespace CalculatorApplication
{
   class NumberManipulator
   {
      public void getValue(out int x )
      {
         int temp = 5;
         x = temp;
      }

      static void Main(string[] args)
      {
         NumberManipulator n = new NumberManipulator();
         /* local variable definition */
         int a = 100;

         Console.WriteLine("Before method call, value of a : {0}", a);

         /* calling a function to get the value */
         n.getValue(out a);

         Console.WriteLine("After method call, value of a : {0}", a);
         Console.ReadLine();

      }
   }
}

исх: Ссылочный параметр - это ссылка на ячейку памяти переменной. Когда вы передаете параметры по ссылке, в отличие от значений параметров, для этих параметров не создается новое место хранения. Эталонные параметры представляют ту же область памяти, что и фактические параметры, которые передаются методу.

В C # вы объявляете ссылочные параметры, используя ключевое слово ref. Следующий пример демонстрирует это:

using System;
namespace CalculatorApplication
{
   class NumberManipulator
   {
      public void swap(ref int x, ref int y)
      {
         int temp;

         temp = x; /* save the value of x */
         x = y;   /* put y into x */
         y = temp; /* put temp into y */
       }

      static void Main(string[] args)
      {
         NumberManipulator n = new NumberManipulator();
         /* local variable definition */
         int a = 100;
         int b = 200;

         Console.WriteLine("Before swap, value of a : {0}", a);
         Console.WriteLine("Before swap, value of b : {0}", b);

         /* calling a function to swap the values */
         n.swap(ref a, ref b);

         Console.WriteLine("After swap, value of a : {0}", a);
         Console.WriteLine("After swap, value of b : {0}", b);

         Console.ReadLine();

      }
   }
}
4 голосов
/ 15 марта 2015

ref и out работают так же, как передача по ссылкам и передача указателями, как в C ++.

Для ref аргумент должен быть объявлен и инициализирован.

Для, аргумент должен быть объявлен, но может или не может быть инициализирован

        double nbr = 6; // if not initialized we get error
        double dd = doit.square(ref nbr);

        double Half_nbr ; // fine as passed by out, but inside the calling  method you initialize it
        doit.math_routines(nbr, out Half_nbr);
3 голосов
/ 29 мая 2017

Время авторизации:

(1) Создаем метод вызова Main()

(2) он создает объект List (который является объектом ссылочного типа) и сохраняет его в переменной myList.

public sealed class Program 
{
    public static Main() 
    {
        List<int> myList = new List<int>();

Во время выполнения:

(3) Среда выполнения выделяет память в стеке в # 00, достаточно широкую для хранения адреса (# 00 = myList, поскольку имена переменных на самом деле являются просто псевдонимами для областей памяти)

(4) Runtime создает список объектов в куче в ячейке памяти #FF (все эти адреса, например, sakes)

(5) Затем среда выполнения сохранит начальный адрес #FF объекта в # 00 (или в словах, сохранит ссылку на объект List в указателе myList)

Назад ко времени авторизации:

(6) Затем мы передаем объект List в качестве аргумента myParamList вызываемому методу modifyMyList и назначаем ему новый объект List

List<int> myList = new List<int>();

List<int> newList = ModifyMyList(myList)

public List<int> ModifyMyList(List<int> myParamList){
     myParamList = new List<int>();
     return myParamList;
}

Во время выполнения:

(7) Runtime запускает подпрограмму вызова для вызываемого метода и как ее часть проверяет тип параметров.

(8) При нахождении ссылочного типа он выделяет память в стеке на # 04 для наложения псевдонима переменной параметра myParamList.

(9) Затем в нем также сохраняется значение #FF.

(10) Runtime создает объект списка в куче в ячейке памяти # 004 и заменяет #FF в # 04 этим значением (или разыменовывает исходный объект List и указывает на новый объект List в этом методе)

Адрес в # 00 не изменяется и сохраняет ссылку на #FF (или оригинальный указатель myList не нарушается).


Ключевое слово ref - это директива компилятора, пропускающая генерацию кода времени выполнения для (8) и (9), что означает, что для параметров метода не будет выделяться куча. Он будет использовать оригинальный указатель # 00 для работы с объектом в #FF. Если исходный указатель не инициализирован, среда выполнения остановится, что приведет к невозможности продолжения работы, поскольку переменная не инициализирована

Ключевое слово out - это директива компилятора, которая почти такая же, как ref с небольшими изменениями в (9) и (10). Компилятор ожидает, что аргумент не будет инициализирован, и продолжит с (8), (4) и (5), чтобы создать объект в куче и сохранить его начальный адрес в переменной аргумента. Неинициализированная ошибка не будет выдана, а все предыдущие сохраненные ссылки будут потеряны.

1 голос
/ 27 июля 2015

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

Из: Ключевое слово out также используется для передачи аргумента, такого как ключевое слово ref, но аргумент может быть передан без присвоения ему значения. Аргумент, который передается с использованием ключевого слова out, должен быть инициализирован в вызываемом методе, прежде чем он вернется к вызывающему методу.

public class Example
{
 public static void Main() 
 {
 int val1 = 0; //must be initialized 
 int val2; //optional

 Example1(ref val1);
 Console.WriteLine(val1); 

 Example2(out val2);
 Console.WriteLine(val2); 
 }

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

/* Output     1     2     

Ссылка и перегрузка в методе перегрузки

И ref, и out нельзя использовать одновременно в перегрузке метода. Тем не менее, ref и out обрабатываются по-разному во время выполнения, но они обрабатываются одинаково во время компиляции (CLR не делает различий между ними, пока он создает IL для ref и out).

1 голос
/ 20 июня 2013

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

int x;    Foo(out x); // OK 
int y;    Foo(ref y); // Error

Параметры Ref предназначены для данных, которые могут быть изменены, параметры out - для данных, которые являются дополнительным выходом для функции (например, int.TryParse), которая уже использует возвращаемое значение для чего-либо.

1 голос
/ 13 ноября 2014
 public static void Main(string[] args)
    {
        //int a=10;
        //change(ref a);
        //Console.WriteLine(a);
        // Console.Read();

        int b;
        change2(out b);
        Console.WriteLine(b);
        Console.Read();
    }
    // static void change(ref int a)
    //{
    //    a = 20;
    //}

     static void change2(out int b)
     {
         b = 20;
     }

Вы можете проверить этот код, он опишет вам его полное отличие когда вы используете "ref", это означает, что вы уже инициализируете int / string

но когда вы используете "Out" это работает в обоих условиях, когда вы инициализируете int / string или нет но вы должны инициализировать эту int / строку в этой функции

0 голосов
/ 25 июня 2013

Ниже я показал пример использования Ref и out . Теперь вы все будете очищены от реф и вне.

В приведенном ниже примере, когда я комментирую // myRefObj = new myClass {Name = "ref outside вызывают !!"}; в строке, появится сообщение об ошибке "Использование неназначенной локальной переменной 'myRefObj'" , но в out .

такой ошибки нет.

Где использовать Ref : когда мы вызываем процедуру с параметром in, и этот же параметр будет использоваться для хранения выходных данных этого процесса.

Где использовать Out: , когда мы вызываем процедуру без параметра in и тот же параметр будет использован для возврата значения из этого процесса. Также обратите внимание на вывод

public partial class refAndOutUse : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        myClass myRefObj;
        myRefObj = new myClass { Name = "ref outside called!!  <br/>" };
        myRefFunction(ref myRefObj);
        Response.Write(myRefObj.Name); //ref inside function

        myClass myOutObj;
        myOutFunction(out myOutObj);
        Response.Write(myOutObj.Name); //out inside function
    }

    void myRefFunction(ref myClass refObj)
    {
        refObj.Name = "ref inside function <br/>";
        Response.Write(refObj.Name); //ref inside function
    }
    void myOutFunction(out myClass outObj)
    {
        outObj = new myClass { Name = "out inside function <br/>" }; 
        Response.Write(outObj.Name); //out inside function
    }
}

public class myClass
{
    public string Name { get; set; }
} 
0 голосов
/ 05 декабря 2016

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

...