34. События. Безымянные методы.
Назначение безымянных методов (anonymous methods) – сократить объем кода, который должен написать разработчик при работе с событиями. Рассмотрим пример, в котором назначаются обработчики событий для объектов класса CExampleClass (подробнее – в параграфе, посвященном работе с событиями).
class MainClass {
public static void firstReaction(int i) {
Console.WriteLine("{0} is a bad value!", i);
}
public static void secondReaction(int i) {
Console.WriteLine("Are you stupid?");
}
public static void Main() {
CExampleClass c = new CExampleClass();
// Назначаем обработчик
c.onErrorEvent += new Proc(firstReaction);
// Назначаем еще один обработчик
c.onErrorEvent += new Proc(secondReaction);
}
}
Мы видим, что для назначения даже простых обработчиков необходимо отдельно описать методы обработки событий и использовать конструкторы соответствующих делегатов. А вот как будет выглядеть тот же код при использовании безымянных методов:
class MainClass {
public static void Main() {
CExampleClass c = new CExampleClass();
// Назначаем обработчик
c.onErrorEvent += delegate(int i) {
Console.WriteLine("{0} is a bad value!", i); };
// Назначаем еще один обработчик
c.onErrorEvent += delegate {
Console.WriteLine("Are you stupid?"); };
}
}
Таким образом, код обработки события помещен непосредственно в место присваивания делегата событию onErrorEvent. Теперь компилятор сам создаст делегат, основываясь на указанной сигнатуре анонимного метода, поместит в этот делегат указатель на код анонимного метода, а сам объект делегата присвоит событию.
Обратите внимание в предыдущем фрагменте кода на второй безымянный метод. Он не использовал параметр события, что позволило не указывать сигнатуру метода после delegate. Компилятор автоматически выполняет соответствующие неявные преобразования для делегатов и безымянных методов. Точные правила совместимости делегата и безымянного метода выглядят следующим образом:
1. Список параметров делегата совместим с безымянным методом, если выполняется одно из двух условий:
a. безымянный метод не имеет параметров, а делегат не имеет out-параметров;
b. список параметров безымянного метода полностью совпадает со списком параметров делегата (число параметров, типы, модификаторы)
2. Тип, возвращаемый делегатом, совместим с типом безымянного метода, если выполняется одно из двух условий:
a. тип делегата – void, а безымянный метод не имеет оператора return или оператор return записан без последующего выражения;
b. выражение, записанное после return в безымянном методе, может быть неявно приведено к типу делегата.
35. Обработка исключений.
Опишем возможности по обработке исключительных ситуаций. Для перехвата исключительных ситуаций служит блок try – catch – finally. Синтаксис блока следующий:
try {
[<команды, способные вызвать исключительную ситуацию>]
}
[<один или несколько блоков catch>]
[finally {
<операторы из секции завершения> }]
Операторы из части finally (если она присутствует) выполняются всегда, вне зависимости от того, произошла исключительная ситуация или нет. Если один из операторов, расположенных в блоке try, вызвал исключительную ситуацию, управление немедленно передается на блоки catch. Синтаксис отдельного блока catch следующий:
catch [(<тип ИС> [<идентификатор объекта ИС>])] {
<команды обработки исключительной ситуации>
}
<идентификатор объекта ИС> – это некая временная переменная, которая может использоваться для извлечения информации из объекта исключительной ситуации. Отдельно описывать эту переменную нет необходимости.
Программа с добавленным в нее блок перехвата ошибки:
class MainClass
{
public static void Main()
{
CExample A = new CExample();
try {
Console.WriteLine("Эта строка печатается");
A.setFx(-3);
Console.WriteLine("Строка не печатается, если ошибка ");
}
catch (MyException ex) {
Console.WriteLine("Ошибка при параметре {0}", ex.Info);
}
finally {
Console.WriteLine("Строка печатается - блок finally");
}
}
}
Если используется несколько блоков catch, то обработка исключительных ситуаций должна вестись по принципу «от частного – к общему», так как после выполнения одного блока catch управление передается на часть finally (при отсутствии finally – на оператор после try – catch). Компилятор C# не позволяет разместить блоки catch так, чтобы предыдущий блок перехватывал исключительные ситуации, предназначенные последующим блокам.