Перейти к содержимому

Свойство или индексатор не могут передаваться как параметр out или ref

  • автор:

ref в сравнении с out в C#

Ключевые слова Ref и out в C# используются для передачи аргументов внутри метода или функции. Оба слова указывают на то, что аргумент/параметр передается по ссылке. По умолчанию параметры передаются в метод по значению. Используя эти ключевые слова ( ref и out ), мы можем передать параметр по ссылке.

Ключевое слово ref

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

Пример кода

public static string GetNextName(ref int id) < string returnText = "Next-" + id.ToString(); id += 1; return returnText; >static void Main(string[] args)

Вывод

Ключевое слово out

Ключевое слово out передает аргументы по ссылке. Это очень похоже на ключевое слово ref .

Пример кода

public static string GetNextNameByOut(out int id) < string returnText = "Next-" + id.ToString(); return returnText; >static void Main(string[] args)

Вывод

Ref в сравнении с Out

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

Инициализация параметра или аргумента перед передачей его в out не является обязательной.

Не требуется присваивать или инициализировать значение параметра (который передается по ref ) перед возвратом в вызывающий метод.

Вызываемый метод обязан присвоить или инициализировать значение параметра (который передается в out ) перед возвратом в вызывающий метод.

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

Объявление параметра в методе out полезно, когда из функции или метода необходимо вернуть несколько значений.

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

Значение параметра должно быть инициализировано в вызывающем методе перед его использованием.

Когда мы используем REF , данные могут передаваться двунаправленно.

Когда мы используем OUT , данные передаются только однонаправленно (от вызываемого метода к вызывающему методу).

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

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

Ключевое слово Ref / Out и перегрузка методов

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

Пример кода

public static string GetNextName(ref int id) < string returnText = "Next-" + id.ToString(); id += 1; return returnText; >public static string GetNextName(out int id)

Вывод при компиляции кода:

Однако перегрузка методов возможна, когда один метод принимает аргумент ref или out , а другой принимает тот же аргумент без ref или out .

Пример кода

public static string GetNextName(int id) < string returnText = "Next-" + id.ToString(); id += 1; return returnText; >public static string GetNextName(ref int id)

Резюме

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

Материал подготовлен в рамках специализации «C# Developer». Если вам интересно узнать подробнее о формате обучения и программе, познакомиться с преподавателем курса — приглашаем на день открытых дверей онлайн. Регистрация здесь.

  • Блог компании OTUS
  • Программирование
  • C#

Индексаторы

Вы знаете, что доступ к индивидуальным элементам, содержащимся в стандартных массивах, осуществляется через операцию индекса ([ ]). В языке C# имеется возможность проектировать специальные классы и структуры, которые могут быть индексированы подобно стандартному массиву, посредством определения индексатора (альтернатива массиву). Если вы можете обойтись массивами, забудьте об индексаторах. Важно, что основное назначение индексатора — предоставить пользователю функциональные возможности, аналогичные работе с массивом. Это языковое средство полезно при создании специальных типов коллекций , когда их размер заранее неизвестен.

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

Одномерные индексаторы

Общая форма одномерного индексатора:

тип_элемента this[int индекс] < get < // Возврат значения, которое определяет индекс. >set < // Установка значения, которое определяет индекс. >>

где тип_элемента обозначает конкретный тип элемента индексатора. Следовательно, у каждого элемента, доступного с помощью индексатора, должен быть определенный тип_элемента. Этот тип соответствует типу элемента массива. Параметр индекс получает конкретный индекс элемента, к которому осуществляется доступ. Формально этот параметр совсем не обязательно должен иметь тип int, но чаще всего используется целочисленный тип данного параметра.

В теле индексатора определены два аксессора (средства доступа к данным, см. также статью о свойствах): get и set. Аксессор подобен методу, за исключением того, что в нем не объявляется тип возвращаемого значения или параметры. Аксессоры вызываются автоматически при использовании индексатора, и оба получают индекс в качестве параметра. Так, если индексатор указывается в левой части оператора присваивания, то вызывается аксессор set и устанавливается элемент, на который указывает параметр индекс. В противном случае вызывается аксессор get и возвращается значение, соответствующее параметру индекс. Кроме того, аксессор set получает неявный параметр value, содержащий значение, присваиваемое по указанному индексу.

Давайте рассмотрим пример:

using System; namespace индексаторы < class MA < object[] ar; public int Leng; public MA(int Size) < ar = new object[Size]; Leng = Size; >// Создаем простейший индексатор public object this[int index] < set < ar[index] = value; >get < return ar[index]; >> > class Program < static void Main() < MA a1 = new MA(Size: 5); // Инициализируем каждый экземпляра класса a1 a1[0] = 5; a1[1] = 3.14; a1[2] = 'Y'; a1[3] = "Russia"; a1[4] = true; for (int i = 0; i < 5; i++) Console.Write("\t", a1[i]); Console.WriteLine("\nДлина - ", a1.Leng); Console.ReadLine(); > > >

В текущем классе MA определен индексатор а1, позволяющий вызывающему коду идентифицировать элементы (объекты) с применением числовых значений индекса (как в массиве). В статье Массивы найдите пример с объявлением массива объектов, сравните коды и найдите отличия.

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

Многомерные индексаторы

Пример индексатора, принимающего несколько параметров:

using System; namespace индексаторы < class MA2 < int[,] ar; // Размерность двухмерного массива public int r, c; public int Leng; // конструктор public MA2(int rows, int cols) < this.r = rows; this.c = cols; ar = new int[this.r, this.c]; Leng = r * c; >// Индексатор public int this[int ind1, int ind2] < get < return ar[ind1, ind2]; >set < ar[ind1, ind2] = value; >> > class Program < static void Main(string[] args) < Random ran = new Random(); MA2 a2 = new MA2(4, 5); for (int i = 0; i < a2.r; i++) < for (int j = 0; j < a2.c; j++) < a2[i, j] = ran.Next(50,90); Console.Write(a2[i, j] + "\t"); >Console.WriteLine(); > Console.ReadLine(); > > >

После инициализации индексатора далее с ним можем работать как с двухмерным массивом целых чисел. Небольшой плюс индексатора: размерности (число строк и столбцов) являются членами класса. Замените 2 строку в методе Main( ) на:

int[,] a2 = new int[4, 5];

т.е. объявим массив a2. Тогда в задании верхних границ индексов будет ошибки типа:

«System.Array» не содержит определения для «r» и не был найден метод расширения «r», принимающий тип «System.Array» в качестве первого аргумента.

Впрочем, если объявите класс МА3:

class MA3 < public int[,] ar; // Размерность двухмерного массива public int r, c; public int Leng; // конструктор public MA3(int rows, int cols) < this.r = rows; this.c = cols; ar = new int[this.r, this.c]; Leng = r * c; >>

и внесете изменения в вызов элементов массива (выделено красным):

class Program < static void Main(string[] args) < Random ran = new Random(); MA3 a2 = new MA3(4, 5); for (int i = 0; i < a2.r; i++) < for (int j = 0; j < a2.c; j++) < a2.ar[i, j] = ran.Next(50,90); Console.Write(a2.ar[i, j] + "\t"); > Console.WriteLine(); > Console.ReadLine(); > >

то вы получите практически тоже, что и с индексатором. Плюс только в том, что вы обращаетесь к элементу индексатора a2[i, j], а к элементу массива через поле ar: a2.ar[i, j].

Примечания.
  1. Современные языки программирования предлагают вам различные варианты реализации тех или иных классов, выбор наиболее удобного – это ваш выбор.
  2. Объявление индексатора очень похоже на объявление свойств с аксессорами set и get. Поэтому понятно, почему свойства и индексаторы относятся к член-функциям класса.

NEW: Наш Чат, в котором вы можете обсудить любые вопросы, идеи, поделиться опытом или связаться с администраторами.

Swap(): свойство или индексатор не могут передаваться как параметр out или ref

Но компилятор выдаёт ошибку. Как сделать Swap(a,b) в таком случае? Mutated[p].Pop() разный при каждом вызове, поэтому я не могу использовать (а,b) = (b,а) , не объявляя дополнительных переменных (да и такая запись сильно удлиняет код и делает его менее понятным, чем Swap(a,b) )

Отслеживать
задан 21 апр 2021 в 17:03
127 11 11 бронзовых знаков
а что с чем оно должно поменять местами?
21 апр 2021 в 17:09

оно должно поменять местами первый параметр и второй ( Swap (a,b) – поменять местами a и b). Напомню, что Mutated[p].Pop() выдаёт разное значение при каждом вызове, поэтому Parent0[Mutated[p].Pop()] != Parent0[Mutated[p].Pop()]

21 апр 2021 в 17:39

1 ответ 1

Сортировка: Сброс на вариант по умолчанию

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

Создайте перегрузку метода Swap . Кстати, называйте в C# методы с большой буквы, так код легче читать.

public static void Swap(IList list, int aIndex, int bIndex)

Использовать можно так

public class MyClass < public int Index < get; set; >public override string ToString() < return Index.ToString(); >> static void Main(string[] args) < Listlist = new List < new MyClass < Index = 1 >, new MyClass < Index = 2 >>; Console.WriteLine(string.Join(", ", list)); Swap(list, 0, 1); Console.WriteLine(string.Join(", ", list)); Console.ReadKey(); > 

Вывод в консоль

1, 2 2, 1 

В вашем случае будет выглядеть так

Swap(Parent0, Mutated[p].Pop(), Mutated[p].Pop()); 

Ключевые слова in, out и ref в методах C#

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

В прошлой части про методы C# было показано использование ключевого слова out в методе. Сегодня разберемся более детально с тем, какие ещё ключевые слова можно использовать в методах C#, в чем их различия, когда надо использовать in , когда ref или out и так далее. Также затронем вопрос об использовании необязательных параметров в методах C#.

Зачем нужны ключевые слова in, out и ref?

Когда только-только разбираться с языком программирования, то невольно возникают сотни различных вопросов. Например, зачем нам вообще нужны ключевые слова in , out и ref в методах C#? Можно же просто передавать в метод определенные параметры и получать в ответ какие-то значения. Давайте рассмотрим простой пример. Допустим, пусть наш метод будет выполнять всего одну операцию — наращивать значение параметра на единицу и возвращать новое значение. Метод будет выглядеть у нас вот так:

static int Addition(int a)

Теперь вызовем наш метод в основной программе, например, вот так:

static void Main(string[] args) < Console.WriteLine("Введите любое целое число и нажмите Enter"); int a = int.Parse(Console.ReadLine()); Console.WriteLine($"Результат выполнения метода: . Значение переменной после выполнения метода: "); >

Что выведет на экран наша программа. Смотрим:

Введите любое целое число и нажмите Enter 1 Результат выполнения метода: 2. Значение переменной после выполнения метода: 1

Как так? Мы же знаем, что арифметический оператор ++ наращивает значение переменной на 1 , но программа нам выдает, что переменная, которую мы передали в параметрах метода осталась неизменной, а результат — верный? На самом деле, здесь в метод мы передаем не саму переменную, а её копию. Значение копии увеличивается на 1 и возвращается нам, а сама переменная остается такой же какой и была. Такой способ передачи параметров называется передачей по значению.

Так вот, ключевые слова in , out и ref в методах позволяют указать нам каким образом мы передаем параметры методу и,в зависимости от того, какое из ключевых слов мы указываем, компилятор и действует. Рассмотрим эти ключевые слова подробнее.

Передача параметров по ссылке. Ключевое слово ref

Ключевое слово ref позволяет передавать аргумент (параметр) по ссылке, а не по значению. Если мы передаем параметр по ссылке, то этот параметр (переменную) можно изменить внутри метода, например, перепишем наш метод Addition :

static int Addition(ref int a)

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

static void Main(string[] args) < Console.WriteLine("Введите любое целое число и нажмите Enter"); int a = int.Parse(Console.ReadLine()); Console.WriteLine($"Результат выполнения метода: . Значение переменной после выполнения метода: "); >

Теперь результат работы программы будет следующий:

Введите любое целое число и нажмите Enter

Результат выполнения метода: 6. Значение переменной после выполнения метода: 6

Как видите, передача параметра по ссылке позволяет изменять значение переменной в теле метода.

Передача параметров по ссылке. Ключевое слово out

Ключевое слово out также используется для передачи аргументов (параметров) по ссылке. Оно похоже на ключевое слово ref , за исключением того, что ref требует инициализации переменной перед ее передачей. Например, если мы попробуем вызвать наш метод вот так:

int b; Addition(ref b);

то компилятор сообщит нам об ошибке:

Ошибка CS0165 Использование локальной переменной «b», которой не присвоено значение.

так как переменная b объявлена не не инициализирована. С ключевым словом out инициализацию переменной можно не проводить перед её передачей в метод и вот такой код не вызовет ошибки:

static int Addition(out int a) < a = 0;//присвоили начальное значение a++; //нарастил на 1 return a; >static void Main(string[] args) < int b; Console.WriteLine($"Результат выполнения метода: . Значение переменной после выполнения метода: "); >

Начиная с версии C# 7.0 переменные, передаваемые по ссылке, можно определять сразу в вызове метода, то есть вызов метода Addition в предыдущем примере можно переписать вот так:

Console.WriteLine($"Результат выполнения метода: . Значение переменной после выполнения метода: ");

Здесь мы сразу при вызове метода определили переменную b .

Передача параметров по ссылке. Ключевое слово in

Кроме выходных параметров метода, объявляемых с модификатором out , в C# можно также определить входные параметры, передаваемые по ссылке с использованием ключевого слова in . Суть ключевого слова in следующая — параметр с модификатором in передается по ссылке, но внутри метода его изменять нельзя. Может возникнуть резонный вопрос: зачем нам тогда такой модификатор? Можно же просто убрать всякие ключевые слова и передавать параметр по значению. Как говориться, дьявол кроется в деталях.

Когда используется передача параметра по значению, то внутри метода создается точная копия переменной, когда по ссылке — в метод передается только адрес в памяти где лежит значение. Когда мы имеем дело с переменными простых типов, типа 5 , 2 , 99.9 , то особых проблем нет — на современных компьютерах мы даже не заметим, что программа «отъела»лишние байты памяти. А теперь представим, что в метод передается структура «весом» с сотню мегабайт. Передача по значению приведет к тому, что у нас в памяти будет сама переменная структуры в 100 Мб и в методе её точная копия ещё на 100 Мб и такой расход памяти уже можно будет ощутить. Поэтому для таких случаев и можно использовать ключевое слово in — передать в метод только ссылку на область памяти где лежит значение и, при этом, запретить это значение изменять.

Необязательные параметры в методах

До сих пор мы использовали в описании методов параметры, которые обязательно было передавать при вызове метода. Однако, C# позволяет также определять в методах необязательные параметры, которые по умолчанию имеют какое-то значение, например, объявим в нашем метода Addition необязательный параметр — значение на которое необходимо увеличить переменную:

static int Addition(out int a, int d = 1)

В вызове метода мы можем не указывать параметр d и при его использовании в теле метода он будет равен 1:

static void Main(string[] args) < Console.WriteLine($"Результат выполнения метода: . Значение переменной после выполнения метода: "); >

Результат выполнения метода: 1. Значение переменной после выполнения метода: 1

А можем вызвать метод вот так:

Console.WriteLine($"Результат выполнения метода: . Значение переменной после выполнения метода: ");

Результат выполнения метода: 5. Значение переменной после выполнения метода: 5

Именованные параметры

По умолчанию в C# параметры в методы должны передаваться ровно в том порядке, в котором они стоят в описании метода. Однако, используя именованные параметры мы можем нарушить порядок по умолчанию, вызвав метод, например, вот так:

Addition(d: 5, a: out b)

Итого

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

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

Добавить комментарий

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