Полное руководство. С# 4.0
Шрифт:
Как пояснялось выше, при неудачном исходе преобразования метод Parse сге нерирует исключение. Для того чтобы избежать генерирования исключений при пре образовании числовых строк, можно воспользоваться методом TryParse, опреде ленным для всех числовых структур. В качестве примера ниже приведен один из вари антов метода TryParse, определяемых в структуре Int32: static bool TryParse(string s, out int результат)
где s обозначает числовую строку, передаваемую данному методу, который возвра щает соответствующий результат после преобразования с учетом выбираемой по умолчанию местной специфики представления чисел. (Конкретную местную специ фику представления чисел с учетом региональных стандартов можно указать в другом варианте данного метода.) При неудачном исходе преобразования, например, когда параметр s не содержит числовую строку в надлежащей
ГЛАВА 15. Делегаты, события и лямбда-выражения
В этой главе рассматриваются три новых средства С#: делегаты, события и лямбда-выражения. Делегат предоставляет возможность инкапсулировать метод, а событие уведомляет о том, что произошло некоторое действие. Делегаты и события тесно связаны друг с другом, поскольку событие основывается на делегате. Оба средства расширяют круг прикладных задача, решаемых при про граммировании на С#. А лямбда-выражение представляет собой новое синтаксическое средство, обеспечивающее упрощенный, но в то же время эффективный способ опре деления того, что по сути является единицей исполняемого кода. Лямбда-выражения обычно служат для работы с деле гатами и событиями, поскольку делегат может ссылаться на лямбда-выражение. (Кроме того, лямбда-выражения очень важны для языка LINQ, описываемого в главе 19.) В данной главе рассматриваются также анонимные методы, ковари антность, контравариантность и групповые преобразования методов. Делегаты
Начнем с определения понятия делегата. Попросту го воря, делегат представляет собой объект, который может ссылаться на метод. Следовательно, когда создается делегат, то в итоге получается объект, содержащий ссылку на метод. Более того, метод можно вызывать по этой ссылке. Иными словами, делегат позволяет вызывать метод, на который он ссылается. Ниже будет показано, насколько действенным оказывается такой принцип.
Следует особо подчеркнуть, что один и тот же делегат может быть использован для вызова разных методов во время выполнения программы, для чего достаточно изменить метод, на который ссылается делегат. Таким образом, метод, вызываемый делегатом, определяется во время выполнения, а не в процессе компиляции. В этом, собственно, и заключается главное преимущество делегата.
ПРИМЕЧАНИЕ Если у вас имеется опыт программирования на C/C++, то вам полезно будет знать, что делегат в C# подобен указателю на функцию в C/C++.
Тип делегата объявляется с помощью ключевого слова delegate. Ниже приведена общая форма объявления делегата: delegate возвращаемый_тип имя(список_параметров);
где возвращаемыйтип обозначает тип значения, возвращаемого методами, которые будут вызываться делегатом; имя — конкретное имя делегата; списокпараметров — параметры, необходимые для методов, вызываемых делегатом. Как только будет соз дан экземпляр делегата, он может вызывать и ссылаться на те методы, возвращаемый тип и параметры которых соответствуют указанным в объявлении делегата.
Самое главное, что делегат может служить для вызова любого метода с соответству ющей сигнатурой и возвращаемым типом. Более того, вызываемый метод может быть методом экземпляра, связанным с отдельным объектом, или же статическим методом, связанным с конкретным классом. Значение имеет лишь одно: возвращаемый тип и сигнатура метода должны быть согласованы с теми, которые указаны в объявлении делегата.
Для того чтобы показать делегат в действии, рассмотрим для начала простой при мер его применения. // Простой пример применения делегата. using System; // Объявить тип делегата. delegate string StrMod(string str); class DelegateTest { // Заменить пробелы дефисами. static string ReplaceSpaces(string s) { Console.WriteLine("Замена пробелов дефисами."); return s.Replace(' ', '-'); } // Удалить пробелы. static string RemoveSpaces(string s) { string temp = ""; int i; Console.WriteLine("Удаление пробелов."); for(i=0; i < s.Length; i++) if(s[i] != ' ') temp += s[i]; return temp; } // Обратить строку. static string Reverse(string s) { string temp = ""; int i, j; Console.WriteLine("Обращение строки."); for(j=0, i=s.Length-1; i >= 0; i--, j++) temp += s[i]; return temp; } 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); } }Вот к какому результату приводит выполнение этого кода. Замена пробелов дефисами. Результирующая строка: Это-простой-тест. Удаление пробелов. Результирующая строка: Этопростойтест. Обращение строки. Результирующая строка: .тсет йотсорп отЭ
Рассмотрим данный пример более подробно. В его коде сначала объявляется деле гат StrMod типа string, как показано ниже. delegate string StrMod(string str);
Как видите, делегат StrMod принимает один параметр типа string и возвращает одно значение того же типа.
Далее в классе DelegateTest объявляются три статических метода с одним пара метром типа string и возвращаемым значением того же типа. Следовательно, они со ответствуют делегату StrMod. Эти методы видоизменяют строку в той или иной фор ме. Обратите внимание на то, что в методе ReplaceSpaces для замены пробелов дефисами используется один из методов типа string — Replace.
В методе Main создается переменная экземпляра strOp ссылочного типа StrMod и затем ей присваивается ссылка на метод ReplaceSpaces. Обратите особое внима ние на следующую строку кода. StrMod strOp = new StrMod(ReplaceSpaces);
В этой строке метод ReplaceSpaces передается в качестве параметра. При этом указывается только его имя, но не параметры. Данный пример можно обобщить: при получении экземпляра делегата достаточно указать только имя метода, на который должен ссылаться делегат. Ясно, что сигнатура метода должна совпадать с той, что указана в объявлении делегата. В противном случае во время компиляции возникнет ошибка.
Далее метод ReplaceSpaces вызывается с помощью экземпляра делегата strOp, как показано ниже. str = strOp("Это простой тест.");
Экземпляр делегата strOp ссылается на метод ReplaceSpaces, и поэтому вы зывается именно этот метод.
Затем экземпляру делегата strOp присваивается ссылка на метод RemoveSpaces, и с его помощью вновь вызывается указанный метод — на этот раз RemoveSpaces. И наконец, экземпляру делегата strOp присваивается ссылка на метод Reverse. А в итоге вызывается именно этот метод.
Главный вывод из данного примера заключается в следующем: в тот момент, когда происходит обращение к экземпляру делегата strOp, вызывается метод, на который он ссылается. Следовательно, вызов метода разрешается во время выполнения, а не в процессе компиляции. Групповое преобразование делегируемых методов
Еще в версии C# 2.0 было внедрено специальное средство, существенно упрощаю щее синтаксис присваивания метода делегату. Это так называемое групповое преобразо вание методов, позволяющее присвоить имя метода делегату, не прибегая к оператору new или явному вызову конструктора делегата.
Ниже приведен метод Main из предыдущего примера, измененный с целью про демонстрировать групповое преобразование методов. static void Main { // Сконструировать делегат, используя групповое преобразование методов. StrMod strOp = ReplaceSpaces; // использовать групповое преобразование методов string str; // Вызвать методы с помощью делегата, str = strOp("Это простой тест."); Console.WriteLine("Результирующая строка: " + str); Console.WriteLine; strOp = RemoveSpaces; // использовать групповое преобразование методов str = strOp("Это простой тест."); Console.WriteLine("Результирующая строка: " + str); Console.WriteLine; strOp = Reverse; // использовать групповое преобразование методов str = strOp("Это простой тест."); Console.WriteLine("Результирующая строка: " + str); Console.WriteLine; }
Обратите особое внимание на то, как создается экземпляр делегата strOp и как ему присваивается метод ReplaceSpaces в следующей строке кода. strOp = RemoveSpaces; // использовать групповое преобразование методов
В этой строке кода имя метода присваивается непосредственно экземпляру деле гата strOp, а все заботы по автоматическому преобразованию метода в тип делегата "возлагаются" на средства С#. Этот синтаксис может быть распространен на любую ситуацию, в которой метод присваивается или преобразуется в тип делегата.