Теория и задания по Си-Шарп (КФУ). Учебное пособие казань 2017 2 удк 681 06 ббк 32. 973 Печатается по постановлению Редакционноиздательского совета
![]()
|
ГЛАВА 5. МЕТОДЫ И ПАРАМЕТРЫ При разработке большинства приложений их разделяют на функциональные модули, так как маленькие разделы кода удобнее для понимания, разработки и отладки. Кроме того, это позволяет несколько раз использовать одни и те же участки кода для других приложений. В С# приложение состоит из классов, которые содержат именованные блоки кода, называемые методами. Метод – это член класса, который может осуществлять действия или вычислять значения. Использование методов В С# программа состоит из классов, содержащих методы. Метод может выполнять действие или вычислять значение. Метод – это набор операторов, которые выполняются вместе. В С# все методы принадлежат какому-либо классу. Для создания метода необходимо задать его имя, определить список параметров и тело метода. Для вызова метода используется имя метода, если у метода есть параметры, то необходимо их определить. Для вызова метода другого класса необходимо, чтобы он был объявлен как public, вызов осуществляется по имени класса и метода. using System; class NestExample { static void Method1 { Console.WriteLine(“Method1”); } static void Method2 { Method1(); Console.WriteLine(“Method2”); Method1(); } 30 static void Main() { Method2(); Method1(); } } В результате получим: Method1 Method2 Method1 Method1 Оператор return останавливает выполнение метода и передаёт управление вызвавшему данный метод оператору. Если метод не void, то необходимо вернуть значение соответствующего типа. Каждый метод имеет набор своих локальных переменных, они видны только в нем и при завершении работы метода уничтожаются. Для того чтобы переменные были видны в нескольких методах класса, необходимо объявить их полями вне метода, но внутри класса. Для не void методов необходимо возвращать значение, каждый «путь выполнения» метода должен заканчиваться оператором return. Для void методов оператор return не обязателен. Использование параметров Параметры позволяют передавать информацию из одного метода в другой. При объявлении метода можно задать список его параметров, если список пустой, то это означает, что метод не имеет параметров. static void MethodWithParameters(int n, string y) { // ... } При вызове метода необходимо задать значения его параметрам. MethodWithParameters(2, "Hello, world"); или 31 int p = 7; string s = "Test message"; MethodWithParameters(p, s); В С# существуют три варианта передачи параметров: по значению, по ссылке и выходные параметры. При передаче параметров по значению, изменение значения параметра в методе не влияет на значение в вызвавшем методе. static void AddOne(int x) { x++; } static void Main() { int k = 6; AddOne(k); Console.WriteLine(k); // Выведет на экран 6, не 7 } При передаче по ссылке передается ссылка на переменную, поэтому все действия, производимые с параметром, оказывают влияние на значение переменной в вызывающем методе. Для каждого параметра по ссылке необходимо указывать ключевое слово ref. До вызова метода необходимо обязательно инициализировать переменную. static void AddOne(ref int x) { x++; } static void Main() { int k = 6; AddOne(ref k); Console.WriteLine(k); // Выведет на экран 7 } Выходные параметры похожи на параметры по ссылке, единственное отличие их состоит в том, что их можно не инициализировать до вызова 32 метода. При передаче выходного параметра перед ним указывается ключевое слово out. static void OutDemo(out int p) { // … } static void Main() { int k; OutDemo(out k); Console.WriteLine(k); } С# позволяет использовать механизм передачи списка параметров изменяемой длины. Для этого используется ключевое слово params. Правило использования: допустим только один список параметров, который должен быть массивом конкретного типа и размещаться последним в общем списке параметров. static long AddList(int k, params long[] v) { long total; long i; for(i = 0, total = 0; i < v.Length; i++) total += v[i]; return total*k; } При вызове можно использовать два пути: вызывать как список отдельных элементов или как массив. static void Main( ) { long x; x = AddList(63, 21, 84); // Список x = AddList(new long[ ]{ 63, 21, 84 }); // Массив } 33 Для выбора вида параметров необходимо учитывать два аспекта: механизм передачи и эффективность. По эффективности передача по значению лучше, чем передачи по ссылке. Когда метод вызывает себя, то это называется рекурсией. Если это происходит при участии другого метода, то это будет неявной рекурсией. Рекурсия часто используется для упрощения логики программы. Перегрузка методов Имя метода не может совпадать с именем любого другого элемента класса, но может совпадать с другим методом – это называется перегрузка метода. Перегружаемые методы – это методы с одинаковыми именами в одном классе. class OverloadingExample { static int Add(int a, intb) { return a + b; } static int Add(int a, intb, int c) { return a + b + c; } static void Main() { Console.WriteLine(Add(1,2) + Add(1,2,3)); } } Нельзя использовать одно имя для метода и переменной, константы или перечисления. class BadMethodNames { static int k; static void k( ) { // ... } } 34 Компилятор использует сигнатуру метода для различия методов в классе. В сигнатуру входят следующие элементы: имя метода, типы параметров, модификаторы параметров. В сигнатуру не входят имена параметров и возвращаемый тип метода. Следующие три метода имеют различную сигнатуру static int LastErrorCode( ) { } static int LastErrorCode(int n) { } static int LastErrorCode(int n, int p) { } Следующие три метода имеют одинаковую сигнатуру static int LastErrorCode(int n) { } static string LastErrorCode(int n) { } static int LastErrorCode(int x) { } Перегружаемые методы полезны, если есть одинаковые методы, различающиеся только списком параметров. class GreetDemo { static void Greet( ) { Console.WriteLine("Hello"); } static void Greet(string Name) { Console.WriteLine("Hello " + Name); } static void Main( ) { Greet( ); Greet("Alex"); } } Кроме того, перегрузки методов полезны при добавлении новой функциональности. Допустим в предыдущем классе, захотим приветствовать пользователя в соответствии со временем дня. 35 class GreetDemo { enum TimeOfDay { Morning, Afternoon, Evening } static void Greet( ) { Console.WriteLine("Hello"); } static void Greet(string Name) { Console.WriteLine("Hello " + Name); } static void Greet(string Name, TimeOfDay td) { string Message = ""; switch(td) { case TimeOfDay.Morning: Message="Good morning"; break; case TimeOfDay.Afternoon: Message="Good afternoon"; break; case TimeOfDay.Evening: Message="Good evening"; break; } Console.WriteLine(Message + " " + Name); } static void Main( ) { Greet( ); Greet("Alex"); Greet("Sandra", TimeOfDay.Morning); } } 36 Вопросы к разделу 1. Объясните что такое методы и почему они важны? 2. Опишите три возможных пути передачи параметров и соответствующие ключевые слова С#. 3. Когда создаются и уничтожаются локальные переменные? 4. Что входит в сигнатуру метода? Лабораторная работа Задания на методы с параметрами и без, различные механизмы передачи параметров. Время, необходимое на выполнение задания 60 мин. Упражнение 5.1 Написать метод, возвращающий наибольшее из двух чисел. Входные параметры метода – два целых числа. Протестировать метод. Упражнение 5.2 Написать метод, который меняет местами значения двух передаваемых параметров. Параметры передавать по ссылке. Протестировать метод. Упражнение 5.3 Написать метод вычисления факториала числа, результат вычислений передавать в выходном параметре. Если метод отработал успешно, то вернуть значение true; если в процессе вычисления возникло переполнение, то вернуть значение false. Для отслеживания переполнения значения использовать блок checked. Упражнение 5.4 Написать рекурсивный метод вычисления факториала числа. Домашнее задание 5.1 Написать метод, который вычисляет НОД двух натуральных чисел (алгоритм Евклида). Написать метод с тем же именем, который вычисляет НОД трех натуральных чисел. Домашнее задание 5.2 Написать рекурсивный метод, вычисляющий значение n-го числа ряда Фибоначчи. Ряд Фибоначчи – последовательность натуральных чисел 1, 1, 2, 3, 5, 8, 13… Для таких чисел верно соотношение 2 1 − − + = k k k F F F 37 ГЛАВА 6. МАССИВЫ И КОЛЛЕКЦИИ Массивы хороший инструмент группировки данных. Однако, массивы хранят фиксированное количество объектов, а иногда заранее не неизвестно, сколько потребуется объектов. И в этом случае намного удобнее применять коллекции. Еще один плюс коллекций состоит в том, что некоторые из них реализует стандартные структуры данных, например, стек, очередь, словарь, которые могут пригодиться для решения различных специальных задач. Большая часть классов коллекций содержится в пространствах имен System.Coll ections (простые необобщенные классы коллекций), System.Collections.Generic (обобщенные или типизированные классы коллекций) и System.Collections.Specialized (специальные классы коллекций). Также для обеспечения параллельного выполнения задач и многопоточного доступа применяются классы коллекций из пространства имен System.Collections.Concurrent. В этой главе рассмотрим использование массивов и обобщенных коллекций. Массивы Синтаксис массивов в С# type[ ] name ; // правильно type name [ ]; // неправильно в C# type[4] name ; // также неправильно C# Для объявления двумерных массивов, используются пустые индексы через запятую. int[,] grid; Для доступа к элементам массива используются индексы в квадратных скобках. Нумерация начинается с нуля. Если размерность массива больше одного, то индексы перечисляются через запятую. long[] row; int[,] grid; … … 38 row[3]; grid[1,2]; В С# индексы массива автоматически проверяются на удовлетворение размерности, при выходе за интервал выдаётся исключение IndexOutOfRangeException. Для проверки размерности можно использовать свойство массива Length и метод GetLength. Сравним массивы с коллекциями. Коллекции гибче массивов, могут хранить различные элементы и имеют переменную длину. Но в связи с этим работа с коллекциями медленнее. ArrayList flexible = new ArrayList( ); flexible.Add("one "); // Добавили строку... flexible.Add (99); // Добавили int Создание коллекции только для чтения ArrayList flexible = new ArrayList( ); ... ArrayList noWrite = ArrayList.ReadOnly(flexible); noWrite [0] = 42; // Исключение при выполнении Объявление массива не создает его, так как массив – это ссылочный тип. При объявлении массива можно не знать его размерность, но при создании знать размер необходимо. Память для массива выделяется последовательно. long[] row = new long[4]; int[,] grid = new int[2,3]; Можно использовать список инициализации при создании массива. Необходимо объявить все элементы, в качестве элементов можно использовать выражения. Те же правила справедливы для многомерных массивов: необходимо определить все строки и в каждой строке все элементы. int[,] data = new int[2,3]{ {2,3,4}, {5,6,7} } Размерность массива можно задавать как константами, так и вычисляемыми значениями. Единственное ограничение при вычисляемой длине, нельзя использовать список инициализации. string s = Console.ReadLine(); 39 int size = int.Parse(s); long[] row = new long[size]; При копировании переменной массива, не создается новый массив, создается новая ссылка на тот же массив. long [] row = new long[4]; long [] copy = row; row [0]++; // изменит copy[0] Рассмотрим некоторые свойства и методы класса System.Array. • Свойство Rank – размерность массива. • Свойство Length – общая длина массива, для многомерных массивов количество всех ячеек. System.Array – класс, от которого неявно наследуют все массивы в С#. • Sort – сортировка массива, поддержка интерфейса IComparable. • Clear – очистка массива, все элементы устанавливаются в NULL. • Clone – создаёт копию массива, копирует все элементы. Этот метод не следит за значениями в элементах. Если там ссылки на объекты, то они просто скопируются, новых объектов создано не будет. • GetLength – по номеру размерности получаем длину массива в этой размерности. • IndexOf – ищет значение в массиве, если нашел возвращает индекс первого вхождения, иначе -1. При возвращении массива из метода его размеры не задаются, скобки остаются пустыми. Если задать, получим ошибку при компиляции. Также с многомерными массивами. static int[,] CreateArray( ) { string s1 = System.Console.ReadLine( ); int rows = int.Parse(s1); string s2 = System.Console.ReadLine( ); int cols = int.Parse(s2); return new int[rows,cols]; } При передаче массива в метод в качестве параметра, новый массив не создается, передается ссылка на тот же массив. Все действия с массивом в 40 методе сохранятся для вызывающего метода. Если нужно избежать этого, необходимо передавать копию массива при помощи метода Array.Copy При вызове приложения из командной строки можно использовать строку параметров, которые разделяются пробелом. Например: C:\> pkzip –add –rec –path=relative c:\code *.cs Тогда если pkzip написан на С#, получим массив string[ ] args = { "-add", "-rec", "-path=relative", "c:\\code", "*.cs" }; Этот массив получим при запуске метода Main. class PKZip { static void Main(string[] args) { } } Для прохода по массиву можно использовать цикл foreach. Тогда не нужен счетчик, проверка длины массива и обращение к элементу. Две следующие конструкции эквивалентны for (int i = 0; i < args.Length; i++) { System.Console.WriteLine(args[i]); } и foreach (string arg in args) { System.Console.WriteLine(arg); } Также foreach можно использовать и для многомерных массивов int[,] numbers = { {0,1,2}, {3,4,5} }; foreach (int number in numbers) { System.Console.WriteLine(number); } 41 Списки – List Класс List Add, которому предоставляется добавляемый элемент. Размер коллекции увеличивается List List Указывать размер коллекции List Коллекция может изменять свои размеры по мере добавления (или удаления) элементов. Но надо иметь в виду, что на физическое добавление элементов уходит время процессора, и при необходимости нужно указать начальный размер. Но если он будет превышен, то в силу необходимости коллекция List List Для удаления из коллекции List RemoveAt можно также удалить элемент, указав его позицию в коллекции List Можно вставить элемент в середину коллекции List List 42 Приведем ряд методов класса List • Add(T) – добавляет объект в конец списка List • AddRange(IEnumerable • Clear()– удаляет все элементы из коллекции List • Contains(T) – определяет, входит ли элемент в коллекцию List • ConvertAll – преобразует элементы текущего списка List • Exists(Predicate • Find(Predicate • FindAll(Predicate • FindIndex(Predicate – выполняет поиск элемента, удовлетворяющего условиям указанного предиката, и возвращает отсчитываемый от нуля индекс первого найденного вхождения в пределах всего списка List • IndexOf(T) – осуществляет поиск указанного объекта и возвращает отсчитываемый от нуля индекс первого вхождения, найденного в пределах всего списка List • Insert(Int32, T) – вставляет элемент в коллекцию List • Remove(T) – удаляет первое вхождение указанного объекта из коллекции List • RemoveAll(Predicate • RemoveAt(Int32) – удаляет элемент списка List • Sort() – сортирует элементы во всем списке List • ToArray() – копирует элементы списка List Многие методы коллекции List 43 Двухсвязные списки – LinkedList Класс коллекций LinkedList В классе LinkedList Вставка элементов осуществляется отличным от List AddLast. Для вставки элемента перед указанным элементом списка или после него можно воспользоваться методами AddBefore и AddAfter. Первый элемент коллекции LinkedList Удаление элемента из коллекции LinkedList Преимущество связного списка проявляется в том, что операция вставки элемента в середину выполняется очень быстро. Это происходит за счет того, что только ссылки Next (следующий) предыдущего элемента и Previous (предыдущий) следующего элемента должны быть изменены так, чтобы указывать на вставляемый элемент. В классе List Естественно, у связных списков есть и свои недостатки. Так, например, все элементы таких списков доступны лишь друг за другом. Поэтому для нахождения элемента, находящегося в середине или конце списка, требуется довольно много времени. Связный список не может просто хранить элементы внутри себя. Вместе с каждым из них ему необходимо иметь информацию о следующем и предыдущем элементах. Поэтому LinkedList 44 элементам списка. Класс LinkedListNode Previous и Value. Свойство List возвращает объект LinkedList Свойство Value типа T возвращает элемент, соответствующий узлу. Если в простом списке List T , то в LinkedList LinkedListNode • Value – значение узла, представленное типом T. • Next – ссылка на следующий элемент типа LinkedListNode • Previous – ссылка на предыдущий элемент типа LinkedListNode Используя методы класса LinkedList • AddAfter(LinkedListNode • AddAfter(LinkedListNode ): вставляет в список новый узел со значением value после узла node. • AddBefore(LinkedListNode • AddBefore(LinkedListNode ): вставляет в список новый узел со значением value перед узлом node. • AddFirst(LinkedListNode ): вставляет новый узел в начало списка. • AddFirst(T value ): вставляет новый узел со значением value в начало списка. • AddLast(LinkedListNode ): вставляет новый узел в конец списка. • AddLast(T value ): вставляет новый узел со значением value в конец списка. • RemoveFirst (): удаляет первый узел из списка. После этого новым первым узлом становится узел, следующий за удаленным. • RemoveLast (): удаляет последний узел из списка. 45 Словари – Dictionary Массив и объекты типа List 4, будучи фактически пятым. Но иногда может понадобиться реализация отображения, при котором используется другой, нецелочисленный тип, например string, double или DateTime. В других языках программирования такая организация хранения данных часто называется ассоциативным массивом. Эта функциональная возможность реализуется в классе Diction ary Dictionary В коллекции Dictionary «ключ–значение» можно воспользоваться системой записи с использованием квадратных скобок, не опасаясь при этом выдачи исключения, даже если ключ уже был добавлен: любое значение с таким же самым ключом будет переписано новым значением. Протестировать наличие в коллекции Dictionary По внутреннему устройству коллекция Dictionary Когда для последовательного обхода элементов коллекции Dictionary Value. Эти элементы доступны только для чтения, и их нельзя использовать для 46 изменения данных в коллекции Dictionary Отметим, что есть схожая необобщенная коллекция – Hashtable, имеющая такой же функционал. Однако эта коллекция проигрывает в скорости при работе с однотипными объектами и используется, как и все необобщенные коллекции, для группировки различных объектов. Относительно производительности рассмотренных здесь коллекций заметим, что добавление нового объекта (Add) быстрее делает List Dictionary List Вопросы к разделу 1. В чем отличия коллекций от массивов? 2. Перечислите основные свойства и методы класса System.Array. 3. Приведите примеры описания массивов и коллекций. 4. Как передавать и возвращать массивы и коллекции из методов. 5. Объясните принцип работы цикла foreach. 6. Скажите о плюсах и минусах использования двусвязных списков. 7. Приведите практические примеры эффективного использования рассмотренных коллекций. Лабораторная работа Задания на массивы, передачу массива в качестве аргумента методу Main. Время, необходимое на выполнение задания 80 мин. Упражнение 6.1 Написать программу, которая вычисляет число гласных и согласных букв в файле. Имя файла передавать как аргумент в функцию Main . Содержимое текстового файла заносится в массив символов. Количество гласных и согласных букв определяется проходом по массиву. Предусмотреть метод, входным параметром которого является массив символов. Метод вычисляет количество гласных и согласных букв. Упражнение 6.2 Написать программу, реализующую умножению двух матриц, заданных в виде двумерного массива. В программе предусмотреть два метода: метод печати матрицы, метод умножения матриц (на вход две матрицы, возвращаемое значение – матрица). 47 Упражнение 6.3 Написать программу, вычисляющую среднюю температуру за год. Создать двумерный рандомный массив temperature[12,30], в котором будет храниться температура для каждого дня месяца (предполагается, что в каждом месяце 30 дней). Сгенерировать значения температур случайным образом. Для каждого месяца распечатать среднюю температуру. Для этого написать метод, который по массиву temperature [12,30] для каждого месяца вычисляет среднюю температуру в нем, и в качестве результата возвращает массив средних температур. Полученный массив средних температур отсортировать по возрастанию. Домашнее задание 6.1 Упражнение 6.1 выполнить с помощью коллекции List Домашнее задание 6.2 Упражнение 6.2 выполнить с помощью коллекций LinkedList Домашнее задание 6.3 Написать программу для упражнения 6.3, использовав класс Dictionary |