Учебное пособие (1077022), страница 23
Текст из файла (страница 23)
Если проверяемое свойство содержит атрибуты данноготипа(методcheckType.GetCustomAttributesвозвращаетколлекциюisAttribute из более чем одного элемента), то метод возвращает истину, а ввыходной параметр метода «out object attribute» помещается первыйэлемент коллекции isAttribute (в соответствии с определением, свойствоNewAttribute может применяться к свойству не более одного раза).Рассмотрим пример, осуществляющий проверку того, что свойстваснабжены атрибутом NewAttribute.
Если атрибут используется, товыводится содержимое поля Description:Type t = typeof(ForInspection);Console.WriteLine("\nСвойства, помеченные атрибутом:");foreach (var x in t.GetProperties()){object attrObj;if (GetPropertyAttribute(x, typeof(NewAttribute), out attrObj)){NewAttribute attr = attrObj as NewAttribute;Console.WriteLine(x.Name + " - " + attr.Description);}}В данном примере в цикле перебираются все свойства классаForInspection.
Если свойство снабжено атрибутом, то выводится названиесвойства и поле Description атрибута.186Информация о свойствеполучаетсяс помощью рассмотреннойфункции GetPropertyAttribute. Для приведения полученного значения типаobject к требуемому типу NewAttribute используется оператор as в форме«выражение as тип».
Оператор as является другой формой приведениятипов кроме рассмотренного ранее приведения типов с помощьюоператора «круглые скобки» в форме «(тип)выражение».Результаты вывода в консоль:Свойства, помеченные атрибутом:property1 - Описание для property1property3 - Описание для property3Атрибуты очень широко применяются в .NET. Например, втехнологии ASP.NET MVC с помощью атрибутов задаются правилапроверки полей ввода при заполнении форм данных.Отметим, что в Java существует аналогичный атрибутам механизм,который называется аннотациями. Вместо квадратных скобок длявыделения аннотаций используется символ «@».8.5 Использованиерефлексиинауровнеоткомпилированных инструкцийКакужебылосказановыше,.NETявляетсяпрограммно-реализованной моделью микропроцессора. Поэтому язык машинныхкоманд MSIL (IL) можно представить в виде команд специфическогодиалекта языка ассемблер.Для дизассемблирования необходимо запустить утилиту ildasm - ILDisassembler.
Данная утилита входит в комплект Visual Studio. Для запусканеобходимо открыть «командную строку разработчика Visual Studio»(данный пункт есть в меню «пуск» в разделе Visual Studio) и набрать вкомандной строке команду «ildasm».Данная утилита является оконной, хотя содержит ключи для запуска вкомандной строке. При запуске без ключей в командной строке187открывается окно, в котором нужно выбрать пункт меню «Файл/Открыть»,азатемвыбрать соответствующийфайлсборки.ВыберемфайлReflection.exe из рассматриваемого в данном разделе примера 14.В результате открывается окно содержащее список классов сборки.Если раскрыть соответствующий класс (в нашем примере ForInspection), тоотобразится список конструкторов, методов, свойств (рис.
23).Рис. 23. Работа ildasm – список классов сборки.188Если дважды нажать левой клавишей мыши на определение любогоэлемента, то детальные команды языка IL отобразятся в отдельном окне.Нажмем дважды на определение метода Plus (рис. 24).Рис. 24. Работа ildasm – код метода.Метод Plus осуществляет сложение двух целых чисел.Пример кода метода на языке C#:public int Plus(int x, int y) { return x + y; }Можно отметить некоторое сходство команд языка IL и командассемблера для Intel x86. Команда add выполняет сложение двух элементовстека виртуальной машины, ldarg загружает параметр метода в стек, retосуществляет выход из метода.Также необходимо отметить, что .NET предоставляет набор классов впространствеименSystem.Reflection.Emit,предназначенныхдлядинамической генерации сборок.
Эти классы могут быть полезны дляразработчиков новых языков на платформе .NET а также для болеедетального знакомства с MSIL. В данное пространство имен входят классыдля создания сборок, классов, методов и др. В частности, класс ILGenerator189позволяет генерировать бинарный код MSIL. Класс OpCodes содержитполный список всех команд MSIL.Существуют программы-декомпиляторы, которые пытаются по кодуMSIL восстановить исходный код на C#, например проект .NET Reflector.Но такому восстановлению могут препятствовать программыобфускаторы,производящиеобфускацию(запутывание)откомпилированного кода. Если сборка подвергнута обфускации, то онаработает корректно. Однако команды MSIL расположены в сборке такимобразом, что декомпилятор не может восстановить по ним болеевысокоуровневые команды на языке C#.
Примером такого проектаявляется Obfuscar.8.6 СамоотображаемостьСамоотображаемость (англ. homoiconicity) – это способность языкапрограммирования анализировать программу на этом языке как структуруданных. В некоторых русскоязычных источниках этот термин дословнопереводят как «гомоиконичность».Термин «самоотображаемость» зачастую используют вместе стермином «метапрограммирование». Самоотображаемость предполагаетанализпрограммкакструктурданных,аметапрограммированиепредполагает генерацию на основе этих структур данных другихпрограмм.Можно сказать, что самоотображаемость – предельный случайрефлексии, так как при этом нет никаких ограничений для анализа. Нетнеобходимости помечать какие-то фрагменты программы атрибутами,читать список методов класса и т.д.
– вся программа является структуройданных, в которой можно производить любой поиск и с которой можновыполнятьлюбыедействия.Втомчислесамоотображаемость190предполагает работу с данными на уровне операторов языка, то есть насамом детальном уровне.В настоящее время язык C# не поддерживает самоотображаемость, ипроблема состоит как раз в детальном уровне. Поскольку язык C# являетсякомпилируемым языком, то с помощью рефлексии можно получитьоткомпилированные команды (как это делалось в предыдущем разделе), нопо ним невозможно на лету восстановить исходные команды C#.Изначально самоотображаемость заявлялась разработчиками C# вчисле основных приоритетов. Однако в настоящее время разработчикизаявляют, что самоотображаемость планируется реализовать в отдаленнойперспективе.Необходимоотметить,чтосамоотображаемостьможетбытьреализована поверх языка с использованием библиотек для работы с AST(Abstract Syntax Tree – абстрактное синтаксическое дерево).
AST даетвозможность представить текст программы в виде дерева, в которомвнутренние вершины соответствуют операторам языка программированияили функциям, а листья соответствуют операндам. Таким образом, ASTпозволяет представить текст программы в виде структуры данных (дерева)и фактически реализует принцип самоотображаемости. В отличие отполучения команд из откомпилированной сборки с использованиемрефлексии, AST-дерево строится по исходному тексту программу. В .NETработа с AST-деревом для прикладных программистов реализуется врамках проекта модульного компилятора Roslyn.В настоящее время наиболее известным языком, имеющим поддержкусамоотображаемости, является язык Lisp, у которого существует большоеколичество диалектов, и который исторически является одним из первыхязыков программирования.
В Lisp программа представляется в видеиерархического списка (программа фактически и есть AST), и каждая191программа может быть обработана другой программой. На платформе.NET реализован один из современных диалектов Lisp – clojure.К самоотображаемым языкам также относятся Elixir (синтаксискоторого похож на Ruby, но при этом реализована самоотображаемость встилеLisp)иProlog(специализированныйязыклогическогопрограммирования).Самоотображаемостьнауровнекомандязыкаближекинтерпретируемым языкам, которые интерпретируются и выполняются науровне исходного кода.
Изначально Lisp был интерпретируемым языком.Самоотображаемость для компилируемого языка требует разработкисложногоспециализированногокомпилятора.Внастоящеевремяпроводятся активные работы по разработке самоотображаемых языков наоснове интерпретируемого языка JavaScript.8.7 Контрольные вопросы к разделу 81. Что такое рефлексия?2. Как реализуется работа со сборками?3. Как получить детальную информацию о типах данных сиспользованием рефлексии?4.
Как выполняются динамические действия с объектами классов?5. Как обеспечивается работа с атрибутами?6. Какможнопросмотретьсодержимоеоткомпилированнойсборки?7. Что такое самоотображаемость в языках программирования?9 Параллельная обработка данныхВ любом языке программирования параллельное программированиетрадиционно считается наиболее сложной темой.
При параллельнойобработке могут возникать сложные и плохо отлаживаемые ошибки,192связанные с одновременной записью в один и тот же набор данных,одновременным обращением к ресурсам и др. Такие сложные случаи здесьне приведены.В этом разделе рассмотрен наиболее простой вариант параллельнойобработки данных, когда в однородном массиве данных необходимоосуществить поиск. В этом случае можно разделить массив данных нанесколько фрагментов и вести поиск параллельно.Параллельная обработка данных рассматривается в данном разделе наоснове фрагментов примера 15.В языке C# существует три вида классов для параллельного запускапотоков: Thread (поток); ThreadPool (пул потоков); Task (задача).КлассThreadявляетсяисторическипервымсредствомдлямногопоточной работы в языке C#.
Он представляет собой практическиполную копию такого же класса в языке Java. Основная проблема классаThread в языке C# – медленный запуск потока, на его создание и запускуходит довольно много времени.Для решения этой проблемы был разработан класс ThreadPool,который содержит пул уже запущенных потоков, которые при запускеначинают выполняться без задержки. Проблема класса ThreadPool – неочень удобный для прикладного программиста набор методов для работы спотоками.С целью преодоления этих неудобств был разработан класс Task.















