Console.WriteLine("The size of int is {0}.", sizeof(int));
Console.WriteLine("The size of long is {0}.", sizeof(long));
}
Тем не менее, если вы хотите получить размер специальной структуры
Point
, то метод
UseSizeOfOperator()
придется модифицировать (обратите внимание на добавление ключевого слова
unsafe
):
unsafe static void UseSizeOfOperator()
{
...
unsafe {
Console.WriteLine("The size of Point is {0}.", sizeof(Point));
}
}
Итак, обзор нескольких более сложных средств языка программирования C# завершен. Напоследок снова необходимо отметить, что в большинстве проектов .NET эти средства могут вообще не понадобиться (особенно указатели). Тем не менее, как будет показано в последующих главах, некоторые средства действительно полезны (и даже обязательны) при работе с API-интерфейсами LINQ, в частности расширяющие методы и анонимные типы.
Резюме
Целью главы было углубление знаний языка программирования С#. Первым делом мы исследовали разнообразные более сложные конструкции в типах (индексаторные методы, перегруженные операции и специальные процедуры преобразования).
Затем мы рассмотрели роль расширяющих методов и анонимных типов. Как вы увидите в главе 13, эти средства удобны при работе с API-интерфейсами LINQ (хотя при желании их можно применять в коде повсеместно). Вспомните, что анонимные методы позволяют быстро моделировать "форму" типа, в то время как расширяющие методы дают возможность добавлять новую функциональность к типам без необходимости в определении подклассов.
Финальная часть главы была посвящена небольшому набору менее известных ключевых слов (
sizeof
,
unsafe
и т.п.); наряду с ними рассматривалась работа с низкоуровневыми типами указателей. Как было установлено в процессе исследования типов указателей, в большинстве приложений C# их
никогда не придется использовать.
Глава 12
Делегаты, события и лямбда-выражения
Вплоть до настоящего момента в большинстве разработанных приложений к операторам верхнего уровня внутри файла
Program.cs
добавлялись разнообразные порции кода, тем или иным способом отправляющие запросы к заданному объекту. Однако многие приложения требуют, чтобы объект имел возможность обращаться
обратно к сущности, которая его создала, используя механизм обратного вызова. Хотя механизмы обратного вызова могут применяться в любом приложении, они особенно важны в графических пользовательских интерфейсах, где элементы управления (такие как кнопки) нуждаются в вызове внешних методов при надлежащих обстоятельствах (когда произведен щелчок на кнопке, курсор мыши наведен на поверхность кнопки и т.д.).
В рамках платформы .NET Core предпочтительным средством определения и реагирования на обратные вызовы в приложении является тип делегата. По существу тип делегата .NET Core — это безопасный в отношении типов объект, "указывающий" на метод или список методов, которые могут быть вызваны позднее. Тем не менее, в отличие от традиционного указателя на функцию C++ делегаты представляют собой классы, которые обладают встроенной поддержкой группового и асинхронного вызова методов.
На заметку! В предшествующих версиях .NET делегаты обеспечивали вызов асинхронных методов с помощью
BeginInvoke()/EndInvoke()
. Хотя компилятор по-прежнему генерирует методы
BeginInvoke()/EndInvoke()
, в .NET Core они не поддерживаются. Причина в том, что шаблон с
IAsyncResult
и
BeginInvoke()/EndInvoke()
, используемый делегатами, был заменен асинхронным шаблоном на основе задач. Асинхронное выполнение подробно обсуждается в главе 15.
В текущей главе вы узнаете, каким образом создавать и манипулировать типами делегатов, а также использовать ключевое слово event языка С#, которое облегчает работу с типами делегатов. По ходу дела вы также изучите несколько языковых средств С#, ориентированных на делегаты и события, в том числе анонимные методы и групповые преобразования методов.
Глава завершается исследованием лямбда-выражений. С помощью лямбда-операции C# (
=>
) можно указывать блок операторов кода (и подлежащие передаче им параметры) везде, где требуется строго типизированный делегат. Как будет показано, лямбда-выражение — не более чем замаскированный анонимный метод и является упрощенным подходом к работе с делегатами. Вдобавок та же самая операция (в .NEТ Framework 4.6 и последующих версиях) может применяться для реализации метода или свойства, содержащего единственный оператор, посредством лаконичного синтаксиса.
Понятие типа делегата
Прежде чем формально определить делегаты, давайте ненадолго оглянемся назад. Исторически сложилось так, что в API-интерфейсе Windows часто использовались указатели на функции в стиле С для создания сущностей под названием функции обратного вызова или просто обратные вызовы. С помощью обратных вызовов программисты могли конфигурировать одну функцию так, чтобы она обращалась к другой функции в приложении (т.е. делала обратный вызов). С применением такого подхода разработчики Windows-приложений имели возможность обрабатывать щелчки на кнопках, перемещение курсора мыши, выбор пунктов меню и общие двусторонние коммуникации между двумя сущностями в памяти.
В .NET и .NET Core обратные вызовы выполняются в безопасной в отношении типов объектно-ориентированной манере с использованием делегатов. Делегат — это безопасный в отношении типов объект, указывающий на другой метод или возможно на список методов приложения, которые могут быть вызваны в более позднее время.
В частности, делегат поддерживает три важных порции информации:
• адрес метода, вызовы которого он делает:
• аргументы (если есть) вызываемого метода:
• возвращаемое значение (если есть) вызываемого метода.
На заметку! Делегаты .NET Core могут указывать либо на статические методы, либо на методы экземпляра.
После того как делегат создан и снабжен необходимой информацией, он может во время выполнения динамически вызывать метод или методы, на которые указывает.
Определение типа делегата в C#
Для определения типа делегата в языке C# применяется ключевое слово
delegate
. Имя типа делегата может быть любым желаемым. Однако сигнатура определяемого делегата должна совпадать с сигнатурой метода или методов, на которые он будет указывать. Например, приведенный ниже тип делегата (по имени
BinaryOp
) может указывать на любой метод, который возвращает целое число и принимает два целых числа в качестве входных параметров (позже в главе вы самостоятельно построите такой делегат, а пока он представлен лишь кратко):