1629295407-c61bfe4caba98380ea3e7cdae6295416 (846200), страница 75
Текст из файла (страница 75)
Если параметр strимеет null-значение, генерируется исключение типа ArgumentNullException, a406Часть I. Язык C#если значение параметра str превышает диапазон, допустимый для типа вызывающегообъекта, — исключение типа OverflowException.Методы синтаксического анализа позволяют легко преобразовать числовое значение,прочитанное в виде строки с клавиатуры или текстового файла, в соответствующийвнутренний формат. Например, следующая программа вычисляет среднее арифметическоеот чисел, введенных пользователем в виде списка.
Сначала пользователю предлагаетсяввести количество усредняемых чисел, а затем программа считывает эти числа с помощьюметода ReadLine() и с помощью метода Int32.Parse() преобразует строки вцелочисленное значение. Затем она вводит значения, используя метод Double. Parse()для преобразования строк в их double-эквиваленты.// Эта программа усредняет список чисел,// введенных пользователем.using System;using System.IO;class AvgNums {public static void Main() {string str; int n;double sum =0.0;double avg, t;Console.Write("Сколько чисел вы собираетесь ввести: ");str = Console.ReadLine();try {n = Int32.Parse(str);}catch(FormatException exc) {Console.WriteLine(exc.Message);n = 0;}catch(OverflowException exc) {Console.WriteLine(exc.Message);n = 0;}Console.WriteLine("Введите " + n + " чисел.");for(int i=0; i < n; i++) {Console.Write(": ");str = Console.ReadLine();try {t = Double.Parse(str);}catch(FormatException exc) {Console.WriteLine(exc.Message);t = 0.0;}catch(OverflowException exc) {Console.WriteLine(exc.Message);t = 0;}sum += t;}avg = sum / n;Console.WriteLine("Среднее равно " + avg);}}Глава 14.
Использование средств ввода-вывода407Вот как могут выглядеть результаты выполнения этой программыСколько чисел вы собираетесь ввести: 5Введите 5 чисел.: 1.1: 2.2: 3.3: 4.4: 5.5Среднее равно 3.3И еще. Вы должны использовать надлежащий метод анализа для типа значения,которое вы пытаетесь преобразовать. Например, попытка использовать методInt32.Parse() для строки, содержащей значение с плавающей точкой, желаемогорезультата не даст.408Часть I.
Язык C#Полныйсправочник поГлава 15Делегаты и событияВэтой главе рассматриваются два новых C#-средства: делегаты и события. Делегатпредоставляет возможность инкапсулировать метод, а событие — это своего родауведомление о том, что имело место некоторое действие. Делегаты и событиясвязанны между собой, поскольку событие создается на основе делегата. Эти средстварасширяют диапазон задач программирования, к которым можно применить язык C#.ДелегатыНачнем с определения термина делегат (delegate).
Делегат — это объект, которыйможет ссылаться на метод. Таким образом, создавая делегат, вы по сути создаете объект,который может содержать ссылку на метод. Более того, этот метод можно вызватьпосредством соответствующей ссылки. Таким образом, делегат может вызывать метод, накоторый он ссылается.На первый взгляд идея ссылки на метод может показаться странной, посколькуобычно мы имеем дело с ссылками, которые указывают на объекты, но в действительностиздесь разница небольшая. Как разъяснялось выше, ссылка по существу представляет собойадрес памяти.
Следовательно, ссылка на объект — это адрес объекта. Даже несмотря на точто метод не является объектом, он тоже имеет отношение к физической области памяти, аадрес его точки входа — это адрес, к которому происходит обращение при вызове метода.Этот адрес можно присвоить делегату. Если уж делегат ссылается на метод, этот методможно вызвать посредством данного делегата.На заметкуЕсли вы знакомы с C/C++, то вам будет полезно узнать, что делегат в C#аналогичен указателю на функцию в C/C++Важно понимать, что во время выполнения программы один и тот же делегат можноиспользовать для вызова различных методов, просто заменив метод, на который ссылаетсяэтот делегат. Таким образом, метод, который будет вызван делегатом, определяется не впериод компиляции программы, а во время ее работы.
В этом и состоит достоинстводелегата.Делегат объявляется с помощью ключевого слова delegate. Общая формаобъявления делегата имеет следующий вид:delegate тип_возврата имя(список_параметров);Здесь элемент тип_возврата представляет собой тип значений, возвращаемыхметодами, которые этот делегат будет вызывать. Имя делегата указывается элементом имя.Параметры, принимаемые методами, которые вызываются посредством делегата, задаютсяс помощью элемента список_параметров. Делегат может вызывать только такиеметоды, у которых тип возвращаемого значения и список параметров (т.е.
его сигнатура)совпадают с соответствующими элементами объявления делегата.Делегат может вызывать либо метод экземпляра класса, связанный с объектом, илистатический метод, связанный с классом,Чтобы увидеть делегат в действии, начнем со следующего простого примера:// Простой пример использования делегата.using System;// Объявляем делегат.delegate string strMod(string str);class DelegateTest {410Часть I. Язык C#static string replaceSpaces(string a) {Console.WriteLine("Замена пробелов дефисами.");return a.Replace(' ', '-');}// Метод удаляет пробелы.static string removeSpaces(string a) {string temp = "";int i;}Console.WriteLine("Удаление пробелов.");for(i=0; i < a.Length; i++)if(a[i] != ' ') temp += a[i];return temp;// Метод реверсирует строку.static string reverse(string a) {string temp = "";int i, j;Console.WriteLine("Реверсирование строки.");for(j=0, i=a.Length-1; i >= 0; i--, j++)temp += a[i];return temp;}public static void Main() {// Создание делегата.strMod strOp = new strMod(replaceSpaces);string str;// Вызываем методы посредством делегата.str = strOp("Это простой тест.");Console.WriteLine("Результирующая строка: " + str);Console.WriteLine();strOp = new strMod(removeSpaces);str = strOp("Это простой тест.");Console.WriteLine("Результирующая строка: " + str);Console.WriteLine();}}strOp = new strMod(reverse);str = strOp("Это простой тест.");Console.WriteLine("Результирующая строка: " + str);Результаты выполнения этой программы выглядят так:Замена пробелов дефисами.Результирующая строка: Это-простой-тест.Удаление пробелов.Результирующая строка: Этопростойтест.Реверсирование строки.Результирующая строка: .тсет йотсорп отЭГлава 15.
Делегаты и события411Итак, в программе объявляется делегат с именем strMod, который принимает одинпараметр типа string и возвращает string-значение. В классе DelegateTestобъявлены три статических метода, сигнатура которых совпадает с сигнатурой, заданнойделегатом.
Эти методы предназначены для модификации строк определенного вида.Обратите внимание на то, что метод replaceSpaces() для замены пробелов дефисамииспользует метод Replace() — один из методов класса string.В методе Main() создается ссылка типа strMod с именем strOp, и ейприсваивается ссылка на метод replaceSpaces(). Внимательно рассмотритеследующую строку:strMod strOp = new strMod(replaceSpaces);Обратите внимание на то, что метод replaceSpaces() передается делегату вкачестве параметра. Здесь используется только имя метода (параметры не указываются).Это наблюдение можно обобщить: при реализации делегата задается только имя метода, накоторый должен ссылаться этот делегат. Кроме того, объявление метода должносоответствовать объявлению делегата.
В противном случае вы получите сообщение обошибке еще во время компиляции.Затем метод replaceSpaces() вызывается посредством экземпляра делегата сименем strOp, как показано в следующей строке:str = strOp("Это простой тест.");Поскольку экземпляр strOp ссылается на метод replaceSpaces(), то вызываетсяименно метод replaceSpaces().
Затем экземпляру делегата strOp присваиваетсяссылка на метод removeSpaces(), после чего strOp вызывается снова. На этот развызывается метод removeSpaces().Наконец, экземпляру делегата strop присваивается ссылка на метод reverse(), иstrOp вызывается еще раз. Это, как нетрудно догадаться, приводит к вызову методаreverse().Главное в этом примере то, что вызов экземпляра делегата strOp трансформируетсяв обращение к методу, на который ссылается strOp при вызове.
Таким образом, решение овызываемом методе принимается во время выполнения программы, а не в периодкомпиляции.Несмотря на то что в предыдущем примере используются статические методы,делегат может также ссылаться на методы экземпляров класса. Однако он должен при этомиспользовать объектную ссылку. Например, вот как выглядит предыдущая программа,переписанная с целью инкапсуляции операций над строками внутри класса StringOps:// Делегаты могут ссылаться также на методы// экземпляров класса.using System;// Объявляем делегат.delegate string strMod(string str);class StringOps {// Метод заменяет пробелы дефисами.public string replaceSpaces(string a) {Console.WriteLine("замена пробелов дефисами.");return a.Replace(' ', '-');}// Метод удаляет пробелы.public string removeSpaces(string a) {string temp = "";int i;412Часть I. Язык C#}}Console.WriteLine("Удаление пробелов.");for(i=0; i < a.Length; i++)if(a[i] != ' ') temp += a[i];return temp;// Метод реверсирует строку.public string reverse(string a) {string temp = "";int i, j;Console.WriteLine("Реверсирование строки.");for(j=0, i=a.Length-1; i >= 0; i--, j++)temp += a[i];return temp;}class DelegateTest {public static void Main() {StringOps so = new StringOps();// Создаем экземпляр// класса StringOps.// Создаем делегат.strMod strOp = new strMod(so.replaceSpaces);string str;// Вызываем методы с использованием делегатов.str = strOp("Это простой тест.");Console.WriteLine("Результирующая строка: " + str);Console.WriteLine();strOp = new strMod(so.removeSpaces);str = strOp("Это простой тест.");Console.WriteLine("Результирующая строка: " + str);Console.WriteLine();}}strOp = new strMod(so.reverse);str = strOp("Это простой тест.");Console.WriteLine("Результирующая строка: " + str);Результаты выполнения этой программы совпадают с результатами предыдущейверсии, но в этом случае делегат ссылается на методы экземпляра класса StringOps.Многоадресатная передачаОдна из самых интересных возможностей делегата — поддержка многоадресатнойпередачи (multicasting).
Выражаясь простым языком, Многоадресатная передача — этоспособность создавать список вызовов (или цепочку вызовов) методов, которые должныавтоматически вызываться при вызове делегата. Такую цепочку создать нетрудно.Достаточно создать экземпляр делегата, а затем для добавления методов в эту цепочкуиспользовать оператор “+=”. Для удаления метода из цепочки используется оператор “-=”.(Можно также для добавления и удаления методов использовать в отдельности операторы“+”, “-“ и “=”, но чаще применяются составные операторы "+=" и "-=".)Глава 15.