В чем разница между ключевыми словами " ref " и "out"?

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

public void myFunction(ref MyClass someClass)

и

public void myFunction(out MyClass someClass)

какой я должен использовать и почему?

24 ответов


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

пока ref две стороны, out не только.


на ref модификатор означает, что:

  1. значение уже установлено и
  2. метод, может читать и изменять его.

на out модификатор означает, что:

  1. значение не установлено и не может быть прочитано методом до он расположен.
  2. метод должны установите его перед возвращением.

предположим, дом появляется в кабине Питера по поводу записки о отчетах TPS.

Если бы дом был аргументом ref, у него была бы печатная копия записки.


Я собираюсь попробовать свои силы в объяснения:

Я думаю, что мы понимаем, как типы значений работает? Типы значений (int, long, struct и т. д.). Когда вы отправляете их в функцию без команды ref, она копирует данные. Все, что вы делаете с этими данными в функции, влияет только на копию, а не на оригинал. Команда ref отправляет фактические данные, и любые изменения повлияют на данные вне функции.

" ок " в запутанной частью, ссылочные типы:

позволяет создать ссылку типа:

List<string> someobject = new List<string>()

когда вы новый someobject, создал две части:

  1. блок памяти, который содержит данные для someobject.
  2. ссылка (указатель) на этот блок данных.

теперь, когда вы посылаете в someobject в метод без ref он копирует ссылка указатель, а не данные. Так вы теперь вот что:

(outside method) reference1 => someobject
(inside method)  reference2 => someobject

две ссылки, указывающие на один и тот же объект. Если вы измените свойство someobject использование reference2 повлияет на те же данные, на которые указывает reference1.

 (inside method)  reference2.Add("SomeString");
 (outside method) reference1[0] == "SomeString"   //this is true

если вы нуль из reference2 или указать новые данные это не повлияет на reference1 ни reference1 точек данных.

(inside method) reference2 = new List<string>();
(outside method) reference1 != null; reference1[0] == "SomeString" //this is true

The references are now pointing like this:
reference2 => new List<string>()
reference1 => someobject

теперь, что происходит, когда вы отправляете someobject по реф методу? The фактическое ссылка to someobject передаются в метод. Итак, у вас есть только одна ссылка на данные:

(outside method) reference1 => someobject;
(inside method)  reference1 => someobject;

но что это значит? Он действует точно так же, как отправка someobject не ref за исключением двух основных вещей:

1) Когда вы обнуляете ссылку внутри метода, она будет обнулять ссылку вне метода.

 (inside method)  reference1 = null;
 (outside method) reference1 == null;  //true

2) Теперь вы можете указать ссылку на совершенно другое местоположение данных и ссылку вне функции теперь будет указывать на новое местоположение данных.

 (inside method)  reference1 = new List<string>();
 (outside method) reference1.Count == 0; //this is true

ref находится в и из.

вы должны использовать out в предпочтении везде, где это достаточно для ваших требований.


out:

В C#, метод может возвращать только одно значение. Если вы хотите вернуть более одного значения, вы можете использовать ключевое слово out. Модификатор out возвращается как return-by-reference. Самый простой ответ заключается в том, что ключевое слово "Выход" используется для получения значения из метода.

  1. вам не нужно инициализировать значение в вызывающую функцию.
  2. вы должны назначить значение в вызываемой функции, иначе компилятор сообщит ошибка.

ref:

В C#, когда вы передаете тип значения, такой как int, float, double и т. д. в качестве аргумента параметра method он передается по значению. Поэтому при изменении значения параметра он не влияет на аргумент в вызове метода. Но если вы пометите параметр ключевым словом" ref", он отразится в фактической переменной.

  1. перед вызовом функции необходимо инициализировать переменную.
  2. Это не обязательно назначьте любое значение параметру ref в методе. Если вы не изменяете значение, зачем отмечать его как "ref"?

расширение примера собаки, кошки. Второй метод с ref изменяет объект, на который ссылается вызывающий объект. Отсюда И "Кот"!!!

    public static void Foo()
    {
        MyClass myObject = new MyClass();
        myObject.Name = "Dog";
        Bar(myObject);
        Console.WriteLine(myObject.Name); // Writes "Dog". 
        Bar(ref myObject);
        Console.WriteLine(myObject.Name); // Writes "Cat". 
    }

    public static void Bar(MyClass someObject)
    {
        MyClass myTempObject = new MyClass();
        myTempObject.Name = "Cat";
        someObject = myTempObject;
    }

    public static void Bar(ref MyClass someObject)
    {
        MyClass myTempObject = new MyClass();
        myTempObject.Name = "Cat";
        someObject = myTempObject;
    }

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

пример:

public void Foo()
{
    MyClass myObject = new MyClass();
    myObject.Name = "Dog";
    Bar(myObject);
    Console.WriteLine(myObject.Name); // Writes "Cat".
}

public void Bar(MyClass someObject)
{
    someObject.Name = "Cat";
}

пока вы проходите в классе, вам не нужно использовать ref Если вы хотите изменить объект внутри метода.


ref и out ведут себя аналогично, за исключением следующих отличий.

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

"Бейкер"

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

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

string s = "Able";

тогда s-это ссылка на класс string, который содержит текст "возможность"! Другое назначение той же переменной через

s = "Baker";

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

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

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

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

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


Out: Оператор 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();

      }
   }
}

ref: Параметр reference-это ссылка на расположение переменной в памяти. Когда ты умрешь ... параметры по ссылке, в отличие от параметров значения, для этих параметров не создается новое хранилище. Опорные параметры представляют то же расположение памяти, что и фактические параметры, поставляемые методу.

в 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();

      }
   }
}

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

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

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

        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);

для тех, кто учится на примере (как я) вот что говорит Антоний Колесов.

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

https://gist.github.com/2upmedia/6d98a57b68d849ee7091


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


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

читать здесь.


Время Создания:

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

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

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

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

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

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

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

назад к авторскому времени:

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

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) среда выполнения запускает процедуру вызова для вызываемого метода и в ее составе проверяет тип параметров.

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

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

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

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


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

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


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

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

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


 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", это означает, что u уже инициализирует этот int/string

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


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

Out: Ключевое слово out также используется для передачи аргумента, такого как ref keyword, но аргумент может быть передан без присвоения ему значения. Аргумент, который передаваемое с помощью ключевого слова 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 не могут использоваться в перегрузке метода одновременно. Однако ref и out обрабатываются по-разному во время выполнения, но они обрабатываются одинаково во время компиляции (CLR не дифференцируется между ними, пока он создавал IL для ref и out).


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

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

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

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

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; }
} 

С точки зрения метода, который получает параметр, разница между ref и out это то, что C# требует, чтобы методы должны писать каждому out параметр перед возвратом и не должен ничего делать с таким параметром, кроме передачи его как out параметр или запись в него, пока он не будет передан как out параметр к другому методу или написанный сразу. Обратите внимание, что некоторые другие языки не предъявляют таких требований; виртуальный или метод интерфейса, который объявлен в C# с


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


Я играл с ref и нашел этот пример довольно интересные. Я думал, зов RefEater(ref s1); вызовет ошибку сборки, как во втором прокомментированном случае, но s1 поле инициализируется значением по умолчанию перед вызовом конструктора (https://stackoverflow.com/a/1920659/5612780).

public class Class1
{
    // this will have the default value
    private string s1;

    public Class1()
    {
        // no issue here..
        RefEater(ref s1);

        // Error CS0165 Use of unassigned local variable 's2'
        //string s2;
        //RefEater(ref s2);
    }

    private void RefEater(ref string s)
    {

    }
}

возможно, я не так хорош в этом, но, конечно, строки (даже если они являются технически ссылочными типами и живут в куче) передаются по значению, а не по ссылке?

        string a = "Hello";

        string b = "goodbye";

        b = a; //attempt to make b point to a, won't work.

        a = "testing";

        Console.WriteLine(b); //this will produce "hello", NOT "testing"!!!!

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

насколько я знаю, вам нужно только ref для структур / типов значений и самой строки, поскольку string является ссылочным типом, который притворяется, что он есть, но не является тип значения.

Я мог бы быть совершенно неправ здесь, хотя, я новичок.


имейте в виду, что ссылочный параметр, который передается внутри функции, непосредственно обрабатывается.

например,

    public class MyClass
    {
        public string Name { get; set; }
    }

    public void Foo()
    {
        MyClass myObject = new MyClass();
        myObject.Name = "Dog";
        Bar(myObject);
        Console.WriteLine(myObject.Name); // Writes "Dog".
    }

    public void Bar(MyClass someObject)
    {
        MyClass myTempObject = new MyClass();
        myTempObject.Name = "Cat";
        someObject = myTempObject;
    }

Это собака, а не кошка. Следовательно, вы должны напрямую работать на someObject.