Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Курс_ред1 С#.doc
Скачиваний:
2
Добавлен:
01.03.2025
Размер:
1.8 Mб
Скачать

Выходные параметры

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

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

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

В качестве примера рассмотрим расширение функции maxValue(), которая возвращает элемент массива с максимальным значением. Модифицируем эту функцию так, чтобы получать индекс элемента массива, содержащего наибольшее значение; в случаях, когда максимальное значение содержится в нескольких элементах, мы будем получать индекс первого максимального элемента. Для этого добавим выходной параметр:

static int maxValue(int[] intArray, out int maxIndex)

{

int maxVal = intArray[0];

maxIndex = 0;

for (int i = 1; i < intArray.Length; i++)

{

if (intArray[i] > maxVal)

{

maxVal = intArray[i];

maxIndex = i;

}

}

return maxVal;

}

Эта функция может быть использована таким образом:

int[] myArray = { -1, 8, 3, 6, 2, 5, -9, 3, 0, 2 };

int maxIndex;

Console.WriteLine("Максимум в массиве myArray равен {0}",

maxValue(myArray, out maxIndex));

Console.WriteLine("Первый раз встречается в элементе {0}",

maxIndex + 1);

Результатом ее выполнения будет следующее:

Обратите внимание, что в тексте программы к значению переменной maxIndex при выводе ее на экран прибавляется единица. Это сделано для того, чтобы придать индексу более удобную для восприятия форму, так как для человека (особенно для не программиста) естественно считать первый элемент массива элементом 1, а не элементом 0.

Область действия переменных

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

Область действия переменной представляет собой очень важное понятие, которое лучше всего проиллюстрировать с помощью примера.

using System;

namespace Function1

{

class Program

{

static void Main(string[] args)

{

string myString = "Строка, определенная в Main()";

Write();

Console.ReadLine();

}

static void Write()

{

Console.WriteLine("myString = {0}", myString);

}

}

}

При компиляции данной программы мы получим сообщение об ошибке и предупреждение. Сообщение об ошибке имеет следующий текст:

error CS0103: The name does not exist in the current context

Смысл его заключается в том, что в данном контексте (имеется в виду текущий класс и пространство имен) переменная с именем myString не определена.

Предупреждение выдается в виде:

warning CS0219: The variable 'myString' is assigned but its value is never used

Это означает, что переменной myString присвоено значение, которое нигде не использовано. Таким образом, имеется два сообщения, на первый взгляд противоречащие друг другу.

Причина проблемы в том, что переменная myString, описанная в основном теле приложения (в функции Main()) , оказывается недоступной в функции Write().

Причина этой недоступности кроется в том, что у переменных имеется область, внутри которой их использование является допустимым. Эта область распространяется на тот блок кода, в котором они описаны, и на все непосредственно вложенные в него блоки кода. Блоки кода функций отделены от блоков кода, из которых они вызываются. Поэтому внутри функции Write() имя myString не считается определенным, а переменная myString, определенная в функции Main(), оказывается неиспользованной в области своего действия, т.к. она может использоваться только в рамках функции Main().

На самом деле в функции Write() мы могли бы использовать совершенно другую переменную с именем myString. Внесем в программу следующие изменения:

using System;

namespace Function2

{

class Program

{

static void Main(string[] args)

{

string myString = "Строка, определенная в Main()";

Write();

Console.WriteLine("\nВнутри функции Main()");

Console.WriteLine("myString = {0}", myString);

Console.ReadLine();

}

static void Write()

{

string myString = "Строка, определенная в Write{)";

Console.WriteLine("Внутри функции Write() ");

Console.WriteLine("myString = {0}", myString);

}

}

}

Этот код успешно компилируется и выдает следующий результат:

Данная программа выполняет следующие действия:

  • В функции Main() описывается и инициализируется переменная с именем myString.

  • Функция Main() передает управление функции Write().

  • В функции Write() описывается и инициализируется переменная с именем myString, отличная от переменной с именем myString, описанной в функции Main().

  • Функция Write() выводит на консоль строку, содержащую то значение переменной myString, которое ей присвоено в функции Write().

  • Функция Write() возвращает управление функции Main().

  • Функция Main() выводит на консоль строку, содержащую то значение переменной myString, которое ей присвоено в функции Main().

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

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

using System;

namespace Function3

{

class Program

{

static string myString;

static void Main(string[] args)

{

string myString = "Строка, определенная в Main()";

Program.myString = "Глобальная строка";

Write();

Console.WriteLine("\nВнутри функции Main()");

Console.WriteLine("Локальная строка myString = {0}", myString);

Console.WriteLine("Глобальная строка myString = {0}",

Program.myString);

Console.ReadLine();

}

static void Write()

{

string myString = "Строка, определенная в Write{)";

Console.WriteLine("Внутри функции Write() ");

Console.WriteLine("Локальная строка myString = {0}", myString);

Console.WriteLine("Глобальная строка myString = {0}",

Program.myString);

}

}

}

И получим следующий результат:

В коде программы появилась еще одна переменная с именем myString, что изменило иерархию имен в программе. Эта переменная описана следующим образом:

static string myString;

Обратите внимание, что при описании этой переменной вновь использовать ключевое слово static. Без этого, к сожалению, переменную невозможно будет использовать ни в одной из двух ранее определенных функций.

Для того чтобы отличать глобальную переменную от локальных переменных с тем же именем в функциях Main() и Write(), необходимо при обращении к глобальной переменной использовать также имя класса, в котором она определена. В данном случае мы обращаемся к глобальной переменной по имени Program.myString.

Обратите внимание, что это необходимо делать только в том случае, если существуют глобальная и локальная переменные с одинаковым именем; если бы локальной переменной с именем myString не существовало, то мы совершенно свободно могли бы использовать для обращения к глобальной переменной имя myString вместо Program.myString. В том случае, когда используется локальная переменная, имя которой совпадает с именем глобальной переменной, говорят, что глобальная переменная является скрытой.

Значение глобальной переменной задается в функции Main():

Program.myString = "Глобальная строка";

и используется в функции Write():

Console.WriteLine("Глобальная строка myString = {0}", Program.myString);

После изучения глобальных переменных может возникнуть вопрос, почему мы всегда не прибегаем к такому способу обмена данными с функциями. Действительно, существуют определенные ситуации, когда этот способ передачи данных оказывается более предпочтительным, однако имеется не меньшее (если не большее) число ситуаций, когда он менее удобен. Стоит ли применять глобальные переменные, зависит от способа использования каждой конкретной функции.

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