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

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

public void myFunction(ref MyClass someClass)

а также

public void myFunction(out MyClass someClass)

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

29 ответов

Решение

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

Так что пока ref это двухстороннее, out это только для

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

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

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

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

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

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

Если бы Дом был спорным аргументом, он заставил бы Питера напечатать новую копию памятки, которую он мог взять с собой.

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

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

Хорошо, к запутанной части, ссылочные типы:

Давайте создадим ссылочный тип:

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

Когда вы создаете новый объект, создаются две части:

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

Теперь, когда вы отправляете некоторый объект в метод без ссылки, он КОПИРУЕТ указатель ссылки, а НЕ данные. Итак, теперь у вас есть это:

(outside method) reference1 => someobject
(inside method)  reference2 => 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 отправляется методу. Итак, теперь у вас есть только одна ссылка на данные:

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

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

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

Реф входит и выходит.

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

из:

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

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

ссылка:

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

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

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


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

Расширяя Собаку, пример Кошки. Второй метод с 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 а также out вести себя аналогично, за исключением следующих различий.

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

Поскольку вы передаете ссылочный тип (класс), нет необходимости использовать 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 и других, чтобы проиллюстрировать это. Я не освещаю лучшие практики, просто примеры, чтобы понять различия.

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

"Бейкер"

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

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

string s = "Able";

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

s = "Baker";

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

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

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

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

РЕДАКТИРОВАТЬ: строка также является неизменной, что означает, что просто не существует метода или свойства, которое изменяет существующий экземпляр строки (вы можете попытаться найти его в документе, но вы не найдете ничего:-)). Все методы обработки строк возвращают новый экземпляр строки! (Вот почему вы часто получаете лучшую производительность при использовании класса StringBuilder)

Для ищущих лаконичного ответа.

Обе ref а также out ключевые слова используются для передачиreference.


Переменная ref ключевое слово должно иметь значение или ссылаться на объект или null до его прохождения.


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

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

В 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 позволяет кому-то другому узнать, что вам от них нужно и что вы собираетесь делать с переменной, которую они предоставляют

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

    • Вызывающий метод увидит такие изменения, как someClass.Message = "Hello World" используете ли вы ref, out или ничего
    • Письмо someClass = new MyClass() внутри myFunction(someClass) заменяет объект, видимый someClass в рамках myFunctionтолько метод. Вызывающий метод все еще знает об исходномMyClass экземпляр, который он создал и передал вашему методу
  • Вам нужно ref или out если вы планируете поменять местами someClass для совершенно нового объекта и хотите, чтобы вызывающий метод видел ваши изменения

    • Письмо someClass = new MyClass() внутри myFunction(out someClass) изменяет объект, видимый методом, вызвавшим myFunction

Есть другие программисты

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

  • С помощью ref делает заявление: "Передайте переменную, которой присвоено какое-то значение, когда вы вызываете мой метод. Имейте в виду, что я могу полностью заменить ее на что-то другое в ходе моего метода. Не ожидайте, что ваша переменная будет указывать на старый объект, когда Я задолбался"

  • С помощью outделает заявление: "Передать переменную-заполнитель моему методу. Неважно, имеет ли она значение или нет; компилятор заставит меня присвоить ему новое значение. Я абсолютно гарантирую, что объект, на который указывает ваша переменная до того, как вы вызвали мой метод, к тому времени, когда я закончу, будет другим

Кстати, в C#7.2 есть in модификатор тоже

И это не позволяет методу заменять переданный экземпляр другим экземпляром. Думайте об этом, как если бы вы сказали миллионам разработчиков: "Передайте мне свою исходную ссылку на переменную, и я обещаю не менять ваши тщательно обработанные данные на что-то другое".in имеет некоторые особенности, а в некоторых случаях, например, когда может потребоваться неявное преобразование, чтобы сделать ваш шорт совместимым с in intкомпилятор временно сделает int, расширит вашу короткую к ней, передаст ее по ссылке и завершит. Он может это сделать, потому что вы заявили, что не собираетесь с ним связываться.


Microsoft сделала это с .TryParse методы для числовых типов:

int i = 98234957;
bool success = int.TryParse("123", out i);

Отметив параметр как outздесь активно заявляют: "Мы определенно собираемся заменить ваше тщательно созданное значение 98234957 на что-то другое"

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

public void PoorlyNamedMethod(out SomeClass x)

Вы можете видеть, что это out, и, таким образом, вы можете знать, что если вы часами обрабатываете числа, создавая идеальный SomeClass:

SomeClass x = SpendHoursMakingMeAPerfectSomeClass();
//now give it to the library
PoorlyNamedMethod(out x);

Что ж, это была пустая трата времени - потратить все эти часы на то, чтобы получить идеальный урок. Его определенно выбросят и заменит PoorlyNamedMethod.

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

(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), чтобы создать объект в куче и сохранить его начальный адрес в переменной аргумента. Неинициализированная ошибка не будет выдана, а все предыдущие сохраненные ссылки будут потеряны.

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

Чтобы проиллюстрировать множество прекрасных объяснений, я разработал следующее консольное приложение:

using System;
using System.Collections.Generic;

namespace CSharpDemos
{
  class Program
  {
    static void Main(string[] args)
    {
      List<string> StringList = new List<string> { "Hello" };
      List<string> StringListRef = new List<string> { "Hallo" };

      AppendWorld(StringList);
      Console.WriteLine(StringList[0] + StringList[1]);

      HalloWelt(ref StringListRef);
      Console.WriteLine(StringListRef[0] + StringListRef[1]);

      CiaoMondo(out List<string> StringListOut);
      Console.WriteLine(StringListOut[0] + StringListOut[1]);
    }

    static void AppendWorld(List<string> LiStri)
    {
      LiStri.Add(" World!");
      LiStri = new List<string> { "¡Hola", " Mundo!" };
      Console.WriteLine(LiStri[0] + LiStri[1]);
    }

    static void HalloWelt(ref List<string> LiStriRef)
     { LiStriRef = new List<string> { LiStriRef[0], " Welt!" }; }

    static void CiaoMondo(out List<string> LiStriOut)
     { LiStriOut = new List<string> { "Ciao", " Mondo!" }; }
   }
}
/*Output:
¡Hola Mundo!
Hello World!
Hallo Welt!
Ciao Mondo!
*/
  • AppendWorld: Копия StringList названный LiStriпередается. В начале метода эта копия ссылается на исходный список и поэтому может использоваться для изменения этого списка. ПозжеLiStri ссылается на другой List<string> объект внутри метода, который не влияет на исходный список.

  • HalloWelt: LiStriRef это псевдоним уже инициализированногоListStringRef. ПройденныйList<string> объект используется для инициализации нового, поэтому ref было необходимо.

  • CiaoMondo: LiStriOut это псевдоним ListStringOut и должен быть инициализирован.

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

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

Out: Ключевое слово 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. и пройдите по ссылке, hense;
       class Program
    {
        public static void Main(string[] args)
        {
            var original = new ObjectWithMememberList(3);
            Console.WriteLine(original.MyList.Capacity); // 3
            ChangeList(original.MyList);
            Console.WriteLine(original.MyList.Capacity); // 3
        }

        static void ChangeList(List<int> vr)
        {
            vr = new List<int>(2);
        }
}

но:

       class Program
    {
        public static void Main(string[] args)
        {
            var original = new ObjectWithMememberList(3);
            Console.WriteLine(original.MyList.Capacity); // 3
            ChangeList(ref original.MyList);
            Console.WriteLine(original.MyList.Capacity); // 2
        }

        static void ChangeList(ref List<int> vr)
        {
            vr = new List<int>(2);
        }
}

и то же самое с out. 2. refаргумент должен быть присваиваемой переменной. курица:

      ChangeList(ref new List<int>()); // Compile Error [might not be initialized before accessing]

но:

      List<int> xs;
ChangeList(out xs); // Compiles

Они практически одинаковы - единственное отличие состоит в том, что переменная, передаваемая в качестве параметра 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", это означает, что вы уже инициализировали эту int/ строку

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

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

В приведенном ниже примере, когда я комментирую //myRefObj = new myClass { Name = "ref outside вызывают!!"}; line, появится сообщение об ошибке "Использование неназначенной локальной переменной 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; }
} 

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

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

С точки зрения вызывающего, C# будет во многих случаях предполагать, что при вызове метода с out Параметр приведет к тому, что переданная переменная будет записана без предварительного чтения. Это предположение может быть неверным при вызове методов, написанных на других языках. Например:

struct MyStruct
{
   ...
   myStruct(IDictionary<int, MyStruct> d)
   {
     d.TryGetValue(23, out this);
   }
}

Если myDictionary идентифицирует IDictionary<TKey,TValue> реализация написана на языке, отличном от C#, хотя MyStruct s = new MyStruct(myDictionary); выглядит как задание, оно может потенциально оставить s неизмененной.

Обратите внимание, что конструкторы, написанные на VB.NET, в отличие от конструкторов на C#, не предполагают, будут ли вызванные методы изменять какие-либо out параметры и очистить все поля безоговорочно. Необычное поведение, упомянутое выше, не произойдет с кодом, написанным полностью на VB или полностью на C#, но может произойти, когда код, написанный на C#, вызывает метод, написанный на VB.NET.

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

ключевое слово ссылки:

ref — это ключевое слово, которое используется для передачи любого значения по ссылке (см. вызов по значению и вызов по ссылке в программировании для получения дополнительных сведений). Короче говоря, вы объявляете и инициализируете значение, например, скажемint age = 5;поэтому этот возраст сохраняется в памяти, занимая место в 4 байта. Теперь, если вы передаете эту возрастную переменную другому методу с помощью ref (что означает передачу их по ссылке, а не по значению), тогда компилятор просто передаст ссылку на эту переменную или, говоря простым языком, адрес памяти места, где находится переменная. сохраняется, и вызываемый метод получает этот адрес и напрямую обращается к данным по этому адресу. Таким образом, очевидно, что любое изменение этих данных произойдет и с переменной, присутствующей в вызывающем методе.

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

Исходящее ключевое слово:

Out и in аналогичны тому, где они оба передают ссылку на переменную. Теперь мы знаем, что оба должны передать ссылку на переменную, совершенно ясно, что в памяти должно быть место, где хранятся байты для переменной. Но не инициализируется в случае выхода. Поскольку требование состоит в том, что вызываемый метод должен инициализировать значение и вернуть его.

Пример: я отправляю адрес сайта stackoverflow своему другу и прошу его создать для меня учетную запись и вернуть учетные данные.

В ключевом слове:

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

Пример: я даю пароль своей учетной записи stackoverflow, но говорю ему ничего не делать, кроме как читать или просматривать сайт. Не задавать ни вопросов, ни ответов, ни голосов, ничего..

Ссылки MSDN:

  1. https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/ref
  2. https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/out-parameter-modifier
  3. https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/in-parameter-modifier

Надеюсь, вышеизложенное понятно.

Я играл с ref и нашел этот пример довольно интересным. Я думал, что зов RefEater(ref s1); вызовет ошибку сборки, как во втором случае с комментариями, но s1 поле инициализируется значением по умолчанию перед вызовом конструктора ( /questions/35395546/initsializatsiya-polya/35395561#35395561).

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

    }
}

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

Например,

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

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

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

        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 для структур / типов значений и самой строки, поскольку строка является ссылочным типом, который притворяется, что он есть, но не является типом значения.

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

Другие вопросы по тегам