Опубликован: 25.09.2008 | Доступ: свободный | Студентов: 3223 / 516 | Оценка: 4.32 / 3.98 | Длительность: 18:50:00
ISBN: 978-5-94774-991-5
Лекция 3:

Основы языка C#

Процедуры и функции

Процедуры и функции C# практически полностью соответствуют их определениям в языке C++. Фактически, в языке С отсутствует определение процедуры, принятое в других языках программирования. Если определить функцию, которая не возвращает никакого результата (тип void) в вызывающую программу, мы получим аналог процедуры в других языках программирования. Основное отличие процедуры от функции состоит в том, что функция должна всегда возвращать некоторый результат, и, кроме того, должна быть вызвана в выражении. Процедура же не возвращает никакого результата, ее вызов осуществляется с помощью оператора языка; кроме того, она имеет входные и выходные аргументы, причем выходных аргументов может быть достаточно много. Таким образом, создавать функцию целесообразно только в том случае, когда необходимо произвести какие-либо вычисления, в результате которых должен быть получен результат в виде одного значения. Процедуру - соответственно в случае, когда может быть получен результат, представляющий собой набор каких-либо значений.

Тем не менее существует ряд нововведений, которые касаются функций, появившихся в C#. Наиболее важной особенностью использования функций и процедур в C# является то, что, так как C# является полностью объектно-ориентированным языком программирования, функции не могут существовать в отрыве от классов. Поэтому функцию библиотек процедур и функций в C# выполняют библиотеки классов.

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

Синтаксис описания методов достаточно прост:

[атрибуты][модификаторы]{void | тип результата функции}
имя метода ([список формальных аргументов])

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

Атрибуты и модификаторы являются очень важной составляющей описания любого метода, тем не менее они будут рассмотрены при описании классов, т. к. имеют к этому самое непосредственное отношение. Пока же можно считать, что под модификаторами подразумеваются модификаторы доступа, из которых мы будем рассматривать пока только два: private и public. Private означает, что данный метод является закрытым, соответственно доступ к нему могут получить только методы того класса, в котором он объявлен. Public - наоборот, означает, что доступ к данному методу является открытым и общедоступным из любой точки приложения.

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

Простейшими примерами описания функций являются следующие:

private void A()
{}

public int B()
{}

  public long Stepen(int a, int b)
  {
    long r;
    r = (long)Math.Pow(a, b);
    return (r);
  }

Здесь метод A является закрытой процедурой, методы B и Stepen - открытыми функциями. У методов A и B не определены формальные параметры, в то время как у метода Stepen два формальных параметра: a и b.

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

[ref|out|params] тип_аргумента имя_аргумента

При этом обязательным является указание типа аргумента, который может быть скалярным, массивом, классом, структурой, - любым типом, допустимым в C#.

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

Все аргументы методов можно разделить на три группы: входные, выходные, обновляемые.

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

В качестве примера слегка модернизируем описанный ранее метод Stepen:

public void Stepen(out long r,int a, int b)
{
  r = (long)Math.Pow(a, b);
}

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

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

long s;
Stepen(out s, 2, 6);
Response.Write(s.ToString());

Обратите внимание на то, что при вызове этого метода первый параметр также указывается с ключевым словом out. Из примера видно, что при завершении работы метода Stepen и возврата в вызывающую программу происходит передача значения переменной r из метода в переменную s, находящуюся в адресном пространстве основной программы.

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

public void Stepen(out long r, int a, params int[] b)
{
  r = 0;
  foreach (int i in b)
    r += (long)Math.Pow(i, a);
}

Вызов метода может быть осуществлен так:

int[] digits ={1,8,4};
Stepen(out s, 2, digits);
Response.Write(s.ToString());

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

Результат работы программы показан на рис. 3.8.

Результат выполнения метода Stepen для расчета значения суммы квадратов чисел

Рис. 3.8. Результат выполнения метода Stepen для расчета значения суммы квадратов чисел

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

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

Процедура в этом случае будет выглядеть следующим образом:

public void Stepen(out long[] r,int a, params int[] b)
{
  r=new long[0];
  Array.Resize(ref r,b.Length);
  int j=0;
  foreach (int i in b)
    r[j++] = (long)Math.Pow(i, a);
}

Здесь необходимо отметить следующие важные особенности. Во-первых, в качестве параметра, возвращающего значение в вызывающую программу, использован массив чисел типа long. Во-вторых, прежде чем станет возможным использовать этот массив, его размер необходимо привести в соответствие с размером массива b. Для этого можно воспользоваться методом Resize объекта Array. Однако этот метод позволяет изменять количество элементов такого массива, для которого это количество уже определено, поэтому перед вызовом метода Resize создается новый массив r, состоящий из ноля элементов. Стоит отметить также то, что перед первым параметром метода Resize находится ключевое слово ref, говорящее о том, что данный метод принимает ссылку на массив r, - именно поэтому внутри метода становится возможным изменение параметров самого массива, а не его копии.

Вызов метода можно осуществить следующим образом:

Response.Write("Результаты вычисления значений массива:<br/>");
 long [] result;
 int [] data={2,3,4,5};
 Stepen(out result, 2, data);
 foreach (long i in result)
Response.Write(i.ToString()+"<br/>");

Результат работы программы изображен на рис. 3.9.

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

Рис. 3.9. Результат работы метода, возводящего в заданную степень массив чисел

Подытоживая все сказанное выше относительно методов, необходимо обратить внимание на следующее. Язык C# является объектно-ориентированным языком программирования. Как известно, основную роль в таких языках играют ссылочные типы, поэтому когда методу передается объект ссылочного типа, все поля этого объекта могут меняться в методе, т. е. программный код метода может получить полный доступ ко всем полям объекта. Это происходит несмотря на то, что формально объект не является выходным и не имеет ключевых слов ref и out, т. е. использует семантику вызова по значению. При таком вызове сама ссылка на объект остается неизменной, но состояние объекта, значения его полей могут полностью измениться. Такая ситуация достаточно распространена и является типичной, поэтому при работе со ссылочными типами данных ключевые слова ref и out нечасто появляются в описании аргументов метода.