КАТЕГОРИИ:
АстрономияБиологияГеографияДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРиторикаСоциологияСпортСтроительствоТехнологияФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника
|
Упрощенный синтаксис № 4: не нужно вручную создавать обертку локальных переменных класса для передачи их в метод обратного вызоваЯ уже показал, как код обратного вызова может ссылаться на другие члены класса. Однако иногда нужно, чтобы код обратного вызова ссылался на локальные параметры или переменные, имеющиеся в определяемом методе. Вот интересный пример: internal sealed class AClass { public static void Using_localVariablesInTheCallbackCode(Int32 numToDo) { // Некоторые локальные переменные. Int32[] squares = new Int32[numToDo]; AutoResetEvent done = new AutoResetEvent(false); // Выполняем некоторые задачи в других потоках. for (Int32 n = 0; n < squares.Length; n++) { ThreadPool.QueueUserWorkItem( delegate(Object obj) { Int32 num = (Int32)obj; // В других условиях на выполнение этой задачи требуется больше времени. squares[num] = num * num; //<<<<< squares – внешняя переменная // Если это последняя задача, оставляем (не закрываем) поток. if (Interlocked.Decrement(ref numToDo) == 0) done.Set(); }, n ); } // Ожидаем завершения всех остальных потоков. done.WaitOne(); // Отображаем результаты. for (Int32 n = 0; n < squares.Length; n++) Console.WriteLine("Index {0}, Square={1}", n, squares[n]); } } В показанном выше методе определяется один параметр, numToDo, и две локальные переменные, squares и done. А код обратного вызова в делегате ссылается на эти переменные. А теперь представьте, что код обратного вызова размещен в отдельном методе (как того требует CLR). Как же передать значения переменных в метод обратного вызова? Единственный способ — определить новый вспомогательный класс, где также определено по полю для каждого значения, которое передается коду обратного вызова. Кроме того, код обратного вызова в этом вспомогательном классе нужно определить как экземплярный метод. В этом случае метод UsingLocalVariablesInTheCallbackCode должен создать экземпляр вспомогательного класса, инициализировать поля значениями его локальных переменных и затем создать объект-делегат, связанный с вспомогательным классом и экземплярным методом. Это очень нудная и чреватая ошибкам работа, которую, конечно же, компилятор C# выполняет за вас автоматически. Показанный выше код компилятор C# переписывает примерно так: internal sealed class AClass { public static void UsingLocalVariablesInTheCallbackCode(Int32 numToDo) { // Некоторые локальные переменные. WaitCallback callback1 = null;
// Создаем экземпляр вспомогательного класса. <>с__DisplayClass2 class1 = new <>c DisplayClass2(); // Инициализируем поля вспомогательного класса. classl.numToDo = numToDo; classl.squares = new Int32[class1.numToDo]; classl.done = new AutoResetEvent(false);
// Выполняем некоторые задачи в других потоках, for (Int32 n = 0; n < classl.squares.Length; n++) { if (callback1 == null) { // Новый объект-делегат привязывается к объекту вспомогательного класса // и к его анонимному экземплярному методу, callback1 = new WaitCallback(classl.<UsingLocalVariablesInTheCallbackCode>b__0); }
ThreadPool.QueueUserWorkItem(callback1, n); } // Ожидаем завершения всех остальных потоков. classl.done.Wait0ne(); // Отображаем результаты. for (Int32 n = 0; n < classl.squares.Length; n++) Console.WriteLine("Index {0}, Square={1}", n, classl.squares[n]); } // Вспомогательному классу присваивается необычное имя, // чтобы избежать возможных конфликтов и // предотвратить доступ из внешнего класса Aclass. [CompilerGenerated] private sealed class <>c__DisplayClass2 : Object { // В коде обратного вызова каждой локальной переменной // соответствует одно открытое поле, public Int32[] squares; public Int32 numToDo; public AutoResetEvent done;
// Открытый конструктор без параметров. public <>с DisplayClass2 { }
// Открытый экземплярный метод, содержащий код обратного вызова. public void <UsingLocalVariablesInTheCallbackCode>b__0(0bject obj) { Int32 num = (Int32) obj; squares[num] = num * num; if (Interlocked.Decrement^ref numToDo) ==0) done.Set(); } } }
Ниже приводится код, в котором использование анонимных методов смотрится исключительно гармоничным и естественным. Без анонимных методов этот код было значительно сложнее писать, читать и поддерживать.
// Создание и инициализация массива String. String[] names = { "Jeff", "Kristin", "Aidan" };
// Извлекаем имена, в которых есть строчная буква 'i'. Char charToFind = 'i'; names = Array.FindAll(names, delegate(String name) { return (name.IndexOf(charToFind) >= 0); });
// Преобразуем все символы строки в верхний регистр. names = Array.ConvertAll<String, String>(names, delegate(String name) { return name.ToUpper(); });
// Сортируем имена. Array.Sort(names, String.Compare);
// Отображаем результаты. Array.ForEach(names, Console.WriteLine);
|