Студопедия

КАТЕГОРИИ:

АстрономияБиологияГеографияДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРиторикаСоциологияСпортСтроительствоТехнологияФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника


Упрощенный синтаксис № 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);

 


Поделиться:

Дата добавления: 2015-04-21; просмотров: 64; Мы поможем в написании вашей работы!; Нарушение авторских прав





lektsii.com - Лекции.Ком - 2014-2024 год. (0.007 сек.) Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав
Главная страница Случайная страница Контакты