При нажатии на Сумма в примере ArbitraryMethod из Лекция 7, VS 2013 выдается ошибка: Необработанное исключение типа "System.InvalidOperationException" в System.Windows.Forms.dll Дополнительные сведения: Недопустимая операция в нескольких потоках: попытка доступа к элементу управления "lblResult" не из того потока, в котором он был создан. Затем: Необработанное исключение типа "System.InvalidOperationException" в mscorlib.dll Дополнительные сведения: Для каждой асинхронной операции метод EndInvoke может вызываться только один раз. |
Введение в windows-формы
Делегаты
Когда вы изучали синтаксис языка C#, то проходили понятие делегатов и событий. Изучение синтаксиса обычно проходит в консольных приложениях, где смысл использования делегатов и событий зачастую остается неясным. Между тем, это основа всего Windows-программирования. Рассмотрим вначале еще раз, что же это такое, а затем интерпретируем наши представления для создания Windows-форм.
В обычной жизни делегат — это дипломатический представитель, наделяемый особыми полномочиями своей организацией. Руководство организации заранее определяет, какие функции (методы) должен выполнить делегат: например, осуществить ежегодную проверку филиала, какие параметры следует передать (передать пакет документов) и что получить в ответ (получить ежегодную отчетность). Событием, инициализирующим работу делегата, может быть завершение рабочего года. Организация может располагать несколькими филиалами и не иметь представления на этапе планирования, куда отправится данный сотрудник в качестве делегата.
Когда мы располагаем на форме кнопку, мы знаем, что при наступлении события — нажатия на кнопку — что-то должно произойти: кнопка должна сообщить объекту или объектам, что мы нажали на нее. Однако, эти объекты еще могут быть не определены. Вместо того чтобы привязывать кнопку к конкретному объекту, мы связываем событие кнопки с делегатом, а когда приложение будет выполняться, назначим делегатом тот метод, который будет необходим.
Запустите Visual Studio .NET. Создайте новое консольное приложение и назовите его Delegate. В классе Class1 объявляем делегат:
class Class1 { delegate void Mydelegate (string s); //…продолжение кода. }
Синтаксис объявления делегата такой: сначала пишем ключевое слово delegate, затем — тип возвращаемого значения ( void в нашем примере), потом — произвольное имя делегата (у нас это Mydelegate ), после которого в круглых скобках перечисляем параметры (у нас только один параметр типа string ). Объявленный делегат появится в окне Class View (со специальным значком для делегатов) (рис. 1.30).
В этом же классе добавим метод, который просто-напросто будет выводить некоторую переменную s на экран:
class Class1
{
delegate void Mydelegate( string s);
static void Metod2В большинстве учебников по программированию, даже написанных на русском языке, принято давать названия объектов на английском языке. В результате достигается железная строгость изложения и путаница у новичков, которые тратят дополнительные усилия на разбор названий, предопределенных средой слов, и поиском их перевода в Lingvo. Конечно, в коммерческих распределенных проектах следует придерживаться общих правил, но для большей ясности далее в некоторых местах я буду писать "Metod" а не "Method", "Chelovek" а не "Man". ( string s)
{
Console.WriteLine(s);
}
//…продолжение кода.
}
Когда мы создаем класс, то вначале мы его объявляем, а затем создаем объект класса (или его экземпляр). Мы объявили делегат Mydelegate, а теперь создадим экземпляр этого делегата del:
[STAThread] static void Main(string[] args) { Mydelegate del = new Mydelegate(Metod); //…продолжение кода. }
Делегат Mydelegate связывается здесь с методом Metod. Теперь созданному экземпляру делегата передадим в качестве параметра переменную типа string, заключенную в кавычки:
[STAThread] static void Main(string[] args) { Mydelegate del = new Mydelegate(Metod); del("переменная типа string, которую принимает Metod, вызываемый делегатом del"); }
Запустите приложение (Ctrl+F5 — для консольных приложений — чтобы иметь возможность его разглядеть) (рис. 1.31).
Как это работает? Экземпляру делегата del мы передаем переменную типа string, заключенную в скобках. Экземпляр del принадлежит делегату Mydelegate, который принимает метод Metod. Этот метод, в свою очередь, принимает переменную string s, которая выводится затем на экран. Обратите внимание, что сигнатуры делегата Mydelegate и метода Metod одинаковы! Если мы изменим тип принимаемой методом переменной на, например, int, среда выдаст ошибку.
using System; namespace Delegate { class Class1 { delegate void Mydelegate( string s); static void Metod( string s) { Console.WriteLine(s); } [STAThread] static void Main(string[] args) { Mydelegate del = new Mydelegate(Metod); del("переменная типа string, которую принимает Metod, вызываемый делегатом del"); } } }Листинг 1.1. Полный листинг проекта Delegate
Создайте новое консольное приложение. Назовите его Delegate2. Поскольку этот пример очень похож на предыдущий, просто приведу полный листинг с комментариями:
using System; namespace Delegate2 { //Создаем новый класс chelovek class chelovek { //Объявляем переменную slovo public string slovo; //Создаем метод govorit public void govorit( string s) { Console.WriteLine( slovo + s); } class Class1 { //Объявляем делегат Mydelegate, который имеет ту же саму сигнатуру, что и метод govorit delegate void Mydelegate(string s); [STAThread] static void Main(string[] args) { //Создаем экземпляр student класса chelovek chelovek student = new chelovek(); //Создаем экземпляр del делегата Mydelegate Mydelegate del = new Mydelegate(student.govorit); //Переменной slovo экземпляра student присваиваем значение "привет" student.slovo = "привет"; // Экземпляру делегата del передаем переменную в кавычках del(" - Сказал студент через делегат"); //Переменной slovo экземпляра student присваиваем значение "пока" student.slovo = "пока"; // Экземпляру делегата del передаем переменную в кавычках del(" - Сказал студент через делегат"); } } } }Листинг 1.2.
В этом примере мы дважды передаем переменную одному экземпляру делегата (рис. 1.32).
Конечно же, здесь тот же самый результат можно было получить без всякого введения делегатов — просто инициализируя переменную slovo. Но пока для нас важно не найти наилучший способ получения заданного результата, а разобраться, как же работают делегаты. Создайте новое консольное приложение и назовите его Matem. Далее опять привожу полный код с комментариями:
using System; namespace Matem { //Создаем новый класс matematika class matematika { //Объявляем переменную a public int a; //Создаем метод calculate public void calculate() { int b = a*a; int c = a*a*a; int d = a*a*a*a; Console.WriteLine ("Само число: " + a + "\nКвадрат: " + b + "\nКуб: " + c + " \nЧетвертая степень:" + d); } } class Class1 { [STAThread] static void Main(string[] args) { //Создаем экземпляр primer класса matematika matematika primer = new matematika(); //Переменной a экземпляра primer присваиваем значение 2 primer.a = 2; //Вызываем метод calculate primer.calculate(); } } }Листинг 1.3.
Результатом выполнения этой программы будет вычисление квадрата, куба и четвертой степени значения переменной (рис. 1.33):
Теперь создадим делегата и его экземпляр (можно создать новое консольное приложение — Matem2 — или изменить существующее):
using System; namespace Matem2 { //Создаем новый класс matematika class matematika { //Объявляем переменную a public int a; //Создаем метод calculate public void calculate(int chislo) { int b = a*a; int c = a*a*a; int d = a*a*a*a; Console.WriteLine ("Само число: " + a + "\nКвадрат: " + b + "\nКуб: " + c + " \nЧетвертая степень:" + d); } } class Class1 { delegate void Mydelegate(int chislo); [STAThread] static void Main(string[] args) { //Создаем экземпляр primer класса matematika matematika primer = new matematika(); //Создаем экземпляр del делегата Mydelegate Mydelegate del = new Mydelegate(primer.calculate); //Переменной a экземпляра primer присваиваем значение 2 primer.a = 2; // Экземпляру делегата del передаем переменную del(1); //Переменной a экземпляра primer присваиваем значение 3 primer.a = 3; // Экземпляру делегата del передаем переменную del(2); //Переменной a экземпляра primer присваиваем значение 4 primer.a = 4; // Экземпляру делегата del передаем переменную del(3); } } }Листинг 1.4.
Результатом выполнения этой программы будет последовательный вывод самого числа, его квадрата, куба и четвертой степени (рис. 1.34):