В C# при отправке параметра методу, когда мы должны использовать «ref» и «out» и когда без них?В C# при отправке параметра методу, когда мы должны использовать «ref» и «out» и когда без них?
ответ
В общем, вам следует избегать использования ref и out, если это возможно.
Это указано, использование ref если метод может понадобиться, чтобы изменить значение. Используйте, когда метод всегда должен присвоить значение.
Разница между ref и out заключается в том, что при использовании компилятор применяет правило, которое вам нужно присвоить параметру out перед возвратом. При использовании ref вы должны присвоить значение переменной до, используя ее как параметр ref.
Очевидно, что вышеизложенное применяется, когда вы пишете свои собственные методы. Если вам нужно вызвать методы, которые были объявлены с помощью модификаторов ref или out по их параметрам, вы должны использовать тот же модификатор перед вашим параметром при вызове метода.
Также помните, что C# передает ссылочные типы (классы) по ссылке (как в, эта ссылка передается по значению). Поэтому, если вы предоставляете некоторый метод ссылочного типа в качестве параметра, метод может изменять данные объекта; даже без ref или out. Но он не может изменить саму ссылку (как в, он не может изменить, на какой объект ссылаются).
Очень простой на самом деле. Вы используете точно такое же ключевое слово, что параметр был первоначально объявлен в методе. Если он был объявлен как out
, вы должны использовать out
. Если он был объявлен как ref
, вы должны использовать ref
.
Они используются в основном для получения нескольких возвращаемых значений из вызова метода. Лично я склонен не использовать их. Если я хочу получить несколько возвращаемых значений из метода, я создам небольшой класс для их хранения.
ref и out используются, когда вы хотите что-то вернуться от метода в этом параметре. Насколько я помню, они оба фактически скомпилированы до того же ИЛ, но C# добавляет некоторые дополнительные вещи, поэтому вы должны быть конкретными.
Вот некоторые примеры:
static void Main(string[] args)
{
string myString;
MyMethod0(myString);
Console.WriteLine(myString);
Console.ReadLine();
}
public static void MyMethod0(string param1)
{
param1 = "Hello";
}
выше не будет компилироваться, так как туЗЬптд никогда не отформатирована. Если myString инициализируется string.Empty, то вывод программы будет пустой строкой, потому что все MyMethod0 это присваивает новую строку локальной ссылке param1.
static void Main(string[] args)
{
string myString;
MyMethod1(out myString);
Console.WriteLine(myString);
Console.ReadLine();
}
public static void MyMethod1(out string param1)
{
param1 = "Hello";
}
myString не инициализирован в методе Main, но программа выдает «Hello». Это связано с тем, что ссылка myString в методе Main обновляется с MyMethod1. MyMethod1 не ожидает, что param1 уже содержит что-либо, поэтому его можно оставить неинициализированным. Тем не менее, метод должен назначать что-то.
static void Main(string[] args)
{
string myString;
MyMethod2(ref myString);
Console.WriteLine(myString);
Console.ReadLine();
}
public static void MyMethod2(ref string param1)
{
param1 = "Hello";
}
Это, опять же, не компилируется. Это связано с тем, что ref требует, чтобы myString в методе Main сначала инициализировалась чем-то. Но, если метод Main изменен так, что myString инициализируется string.Empty, тогда код будет компилироваться, а выход будет Hello.
Таким образом, разница не может быть использована с неинициализированным объектом, ref должен быть передан инициализированному объекту. И если вы передаете объект без ссылки на него, его нельзя заменить.
Только для того, чтобы быть ясным: если передаваемый объект уже является ссылочным типом, тогда метод может обновлять объект, а обновления отражаются в вызывающем коде, однако ссылка на объект не может быть изменена. Так что, если я пишу такой код:
static void Main(string[] args)
{
string myString = "Hello";
MyMethod0(myString);
Console.WriteLine(myString);
Console.ReadLine();
}
public static void MyMethod0(string param1)
{
param1 = "World";
}
Выход из программы будет Здравствуйте, а не World, так как метод только изменил свою локальную копию справки, не ссылка, которая была передана в
.Надеюсь, это имеет смысл. Мое общее правило - просто не использовать их. Я чувствую, что это возврат к дням до ОО. (Но, это только мое мнение)
Отличный ответ !!! – micmoo
Колин, большое спасибо .... но предположим, что вместо использования «param1» в качестве строки (обе из нас знают, что строка является ссылочным типом) мы передаем DataTable вашему последнему методу ... удивительно, наш DataTable может иметь несколько строк в нем (конечно, если метод хочет заполнить его), и мы вернемся к основному телу программы, мы можем что-то сделать с нашими строками DataTable. твоя идея? – odiseh
Кроме того, не могли бы вы сказать несколько примеров того, что вы сказали вначале: «Лично я склонен не использовать их. Если мне нужно несколько возвращаемых значений из метода, я создам небольшой класс для их хранения». – odiseh
(это является дополнением к существующим ответам - несколько дополнительных соображений)
Существует еще один сценарий для использования ref
с C#, более часто видели в таких вещах, как XNA ... Обычно, когда вы передаете значение типа (struct
), оно клонируется. Это использует пространство стека и несколько циклов процессора, и имеет побочный эффект, что любые изменения в struct
в вызываемом методе теряются.
(в сторону: обычно struct
s должны быть неизменны, но изменяемых Структуры не редкость в XNA)
Чтобы обойти эту проблему, довольно часто можно увидеть ref
в таких программах. не
Но в наиболее программы (то есть, где вы используете class
эс как по умолчанию), вы можете обычно просто передать ссылку «по значению» (т.е. не ref
/out
).
Другого очень общего пользование, случай out
является Try*
шаблоном, например:
string s = Console.ReadLine();
int i;
if(int.TryParse(s, out i)) {
Console.WriteLine("You entered a valid int: " + i);
}
Или так же, TryGetValue
на словаре.
Это может использовать кортеж вместо этого, но это такой общий шаблон, который разумно понимают даже люди, которые слишком много борются с ref
/out
.
Старайтесь избегать использования реф. Out хорошо, потому что вы знаете, что произойдет, старое значение исчезнет, и новое значение будет в вашей переменной, даже если функция не удалась. Однако, просто взглянув на функцию, вы не представляете, что произойдет с параметром ref. Это может быть тот же, измененный или совершенно новый объект.
Когда я вижу реф, я нервничаю.
ref следует избегать (я верю, что для этого также существует правило fx-cop), однако использовать ref, когда объект, который является ссылкой, может сам измениться. Если вы видите ключевое слово 'ref', вы знаете, что базовый объект больше не может ссылаться на одну и ту же переменную после вызова метода.
В дополнение к подробному ответу Колина вы также можете использовать параметры для возврата нескольких значений из одного вызова метода. См. Например, метод ниже, который возвращает 3 значения.
static void AssignSomeValues(out int first, out bool second, out string third)
{
first = 12 + 12;
second = false;
third = "Output parameters are okay";
}
Вы можете использовать его как так
static void Main(string[] args) {
int i;
string s;
bool b;
AssignSomeValues(out i, out b, out s);
Console.WriteLine("Int value: {0}", i);
Console.WriteLine("Bool value: {0}", b);
Console.WriteLine("String value: {0}", s);
//wait for enter key to terminate program
Console.ReadLine(); }
Просто убедитесь, что вы назначаете допустимое значение для каждого из параметров, чтобы избежать ошибки.
Я предположил, что одиозе это означает, когда вы пишете свои собственные методы, но я мог ошибаться! – RichardOD
точно, ты прав! – odiseh