Что такое Reflection и когда это хороший подход? - PullRequest
17 голосов
/ 14 мая 2009

Что именно является отражением? Я прочитал статью на эту тему в Википедии и понял, что это своего рода метапрограммирование, когда программа может изменять себя во время выполнения, но что это значит? В каких ситуациях это хороший подход и когда его лучше всего использовать?

Ответы [ 11 ]

24 голосов
/ 14 мая 2009

Отражение - это средство, где вы можете запросить объект об его атрибутах во время выполнения. Например, Python, Java и .Net имеют средства, где вы можете найти переменные экземпляра или методы объекта.

Примером приложения для отражения является слой отображения O / R. Некоторые используют отражение для конструирования объекта путем запроса его свойств во время выполнения и динамического заполнения экземпляра. Это позволяет вам делать это программно на основе метаданных из какого-либо словаря данных без перекомпиляции приложения.

Чтобы взять простой пример, я буду использовать Python, потому что его средства отражения очень просты в использовании и требуют меньше шаблонного, чем в java или .Net.

ActivePython 2.5.2.2 (ActiveState Software Inc.) based on
Python 2.5.2 (r252:60911, Mar 27 2008, 17:57:18) [MSC v.1310 32 bit (Intel)] on
win32
Type "help", "copyright", "credits" or "license" for more information.
>>> class foo:
...     def __init__(self):
...             self.x = 1
...
>>> xx = foo()      # Creates an object and runs the constructor
>>> xx.__dict__     # System metadata about the object
{'x': 1}
>>> a = xx.__dict__ # Now we manipulate the object through 
>>> a['y'] = 2      # its metadata ...
>>> print xx.y      # ... and suddenly it has a new instance variable
2                   
>>>

Теперь мы использовали базовое отражение, чтобы изучить переменные экземпляра произвольного объекта. Специальная переменная __dict__ в Python - это системное свойство объекта, в котором хеш-таблица его членов содержит имя переменной (или метода). Мы рефлекторно исследовали объект и использовали средства отражения, чтобы искусственно вставить в него вторую переменную экземпляра, которую мы затем можем отобразить, вызвав ее как переменную экземпляра.

Обратите внимание, что этот конкретный прием не работает на Java или .Net, поскольку переменные экземпляра являются фиксированными. Система типов этих языков не позволяет добавлять новые переменные экземпляра во время выполнения так, как это делает система ввода Python 'duck'. Однако вы могли бы рефлексивно обновить значение переменной экземпляра, которая была объявлена ​​в определении типа.

Вы также можете использовать отражение, чтобы динамически создавать вызовы методов и выполнять различные другие хитрые приемы, такие как создание объекта на основе параметра. Например, если у вас была какая-то система на основе плагинов, в которой определенные возможности были необязательными, вы можете использовать отражение, чтобы запросить плагин о том, какие услуги он предлагает (возможно, путем запроса о том, были ли реализованы определенные интерфейсы), не требуя явных метаданных.

Многие динамические языковые интерфейсы, такие как OLE-автоматизация, используют отражение как неотъемлемую часть интерфейса.

20 голосов
/ 14 мая 2009

Это не так много , изменяющих код во время выполнения, но проверяющих объекты и просящих их выполнить код, не зная статически их тип.

Одним из простых способов описать это было бы «несколько болезненный способ заставить статически типизированный язык вести себя динамически».

РЕДАКТИРОВАТЬ: использует:

  • Конфигурация (например, взять файл XML, который определяет типы и свойства, а затем создать соответствующие объекты)
  • Тестирование (модульные тесты, которые идентифицируются по имени или атрибутам)
  • Веб-сервисы (по крайней мере, в .NET, в основном ядре веб-сервисов используется много размышлений)
  • Автоматическое связывание событий - укажите метод с соответствующим именем, например, SubmitButton_Click и ASP.NET присоединит этот метод в качестве обработчика для события SubmitButton Click (если у вас включена автопроводка)

Это хорошая идея? Ну, только когда альтернативы болезненны. Я предпочитаю статическую типизацию, когда она не мешает - тогда вы получаете много хорошего во время компиляции, и это тоже быстрее. Но когда вам нужно это нужно, рефлексия позволяет вам делать разные вещи, которые иначе просто были бы невозможны.

3 голосов
/ 14 мая 2009

Отражение было полезно для меня по крайней мере в 1 проекте, о котором я могу подумать. Мы написали внутреннюю программу «Диспетчер процессов», которая через определенные промежутки времени выполняет множество ключевых бизнес-процессов. Проект настроен так, что ядро ​​на самом деле представляет собой просто службу Windows с объектами таймера, которые запускаются каждые 30 секунд или около того и проверяют работу.

Фактическая работа выполняется в библиотеке классов (соответственно называется "WorkerLib"). Мы определяем классы с помощью открытых методов, которые выполняют определенные задачи (перемещение файлов, загрузка данных на удаленные сайты и т. Д.). Идея заключается в том, что основная служба может вызывать методы из рабочей библиотеки, ничего не зная о вызываемых ею методах. Это позволяет нам создавать расписание в базе данных для заданий и даже добавлять новые методы в библиотеку классов без необходимости изменять основную систему.

Основная идея заключается в том, что мы можем использовать отражение в основном сервисе для выполнения методов, имена которых мы сохранили в базе данных, определяющей расписания. Это довольно аккуратно на практике. Наша базовая служба надежна и обрабатывает задачи по мере необходимости, в то время как реальная библиотека работника может быть расширена и изменена по мере необходимости, даже не заботясь о ядре.

Если вам нужны дальнейшие объяснения, пожалуйста, не стесняйтесь спрашивать. Это был просто лучший способ объяснить сценарий реального мира, в котором рефлексия действительно облегчает нам задачу.

3 голосов
/ 14 мая 2009

Первый хороший пример, который я могу вспомнить, - это когда вам нужно выполнить набор методов для данного объекта, не зная во время компиляции, какие методы будут в нем существовать.

Возьмем, например, фреймворки для юнит-тестирования. Класс тестового прогона, отвечающий за выполнение всех ваших модульных тестов, не знает заранее, как вы будете называть свои методы. Все, что он знает, это то, что они будут иметь префикс «test» (или в случае Java 5, помеченный @Test). Поэтому, когда ему предоставляется тестовый класс, он отражается на этом классе, чтобы получить список всех методов в нем. Затем он перебирает имена этих методов в виде строк и вызывает эти методы объекта, если они начинаются с «test». Это было бы невозможно без отражения. И это только один пример.

2 голосов
/ 14 мая 2009

Действительно, отражение следует рассматривать как своего рода усилитель кода. Это может сделать хороший код лучше и чище, а плохой код хуже. Что оно делает? Это действительно позволяет вам писать код, что вы не совсем уверены, что он собирается делать во время написания. У вас есть общее представление, но оно позволяет вам не кодировать, какие объекты, методы и свойства будут выполняться при компиляции программы.

Другие сообщения верны, когда они говорят, что это позволяет программе выполнять код на основе значений конфигурации, но его реальная сила в том, что она позволяет вам серьезно нарушать правила объектно-ориентированного программирования. Это действительно то, что он делает. Это как отключение мер безопасности. К частным методам и свойствам можно получить доступ посредством рефлексии наряду со всем остальным.

Отличным примером того, когда MS использует отражение, является привязка данных к объекту данных. Вы указываете имя текстового поля и поля данных для объекта, который нужно привязать к выпадающему списку и т. Д., А код отражает объект и извлекает соответствующую информацию. Объект привязки данных делает один и тот же процесс снова и снова, но он не знает, с каким типом объекта он должен быть связан. Reflection - это удобный способ написать немного кода для обработки всех возможных случаев.

2 голосов
/ 14 мая 2009

Другой пример: у меня есть код, который я использую, который берет выходные данные базы данных, которая представляет собой набор строк с именованными столбцами, и передает их в массив объектов. Я перебираю строки, и если целевой объект имеет свойство с тем же именем и типом, я его устанавливаю. Это делает мой код для получения данных просто похожим на это:

SqlDataReader sdr = Helper.GetReader("GetClientByID", ClientID);
Client c = new Client();
FillObject(sdr, c);
return c;
1 голос
/ 21 июля 2011

Сборки содержат модули, модули содержат типы, а типы содержат элементы. Reflection предоставляет объекты, которые инкапсулируют сборки, модули и типы. Вы можете использовать отражение, чтобы динамически создавать экземпляр типа, связывать тип с существующим объектом или получать тип от существующего объекта. Затем вы можете вызвать методы типа или получить доступ к его полям и свойствам. Типичное использование отражения включает следующее:

  • Используйте сборку для определения и загрузки сборок, загрузки модулей, перечисленных в манифесте сборки, и найдите тип в этой сборке и создайте его экземпляр.
  • Используйте Модуль для обнаружения информации, такой как сборка, которая содержит модуль, и классы в модуле. Вы также можете получить все глобальные методы или другие специфические, неглобальные методы, определенные в модуле.
  • Используйте ConstructorInfo для обнаружения информации, такой как имя, параметры, модификаторы доступа (например, общедоступные или частные) и подробности реализации (например, абстрактные или виртуальные) конструктора. Используйте метод GetConstructors или GetConstructor типа для вызова определенного конструктора.
  • Используйте MethodInfo для обнаружения информации, такой как имя, тип возвращаемого значения, параметры, модификаторы доступа (например, открытый или закрытый) и подробности реализации (например, абстрактный или виртуальный) метода. Используйте метод GetMethods или GetMethod типа для вызова определенного метода.
  • Используйте FieldInfo для обнаружения информации, такой как имя, модификаторы доступа (например, общедоступные или частные) и подробности реализации (например, статические) поля, а также для получения или установки значений поля.
  • Использование EventInfo для обнаружения информации, такой как имя, тип данных обработчика событий, пользовательские атрибуты, тип объявления и отраженный тип события, а также для добавления или удаления обработчиков событий.
  • Используйте PropertyInfo для обнаружения информации, такой как имя, тип данных, декларируемый тип, отраженный тип и статус свойства, доступного только для чтения или записи, а также для получения или установки значений свойства.
  • Используйте ParameterInfo для обнаружения информации, такой как имя параметра, тип данных, является ли параметр входным или выходным параметром и положение параметра в сигнатуре метода.
1 голос
/ 14 мая 2009

Отражение - это (в основном) способность программы запрашивать информацию о типе, которая была доступна компилятору. Так, например, учитывая имя типа, вы можете запросить методы, которые он содержит. Затем для каждого метода вы можете запросить типы параметров, которые они принимают и т. Д. И т. Д. И т. Д.

Это полезно для настройки во время выполнения, когда у вас есть файл конфигурации, который определяет поведение вашего приложения. Конфигурация может содержать имена конкретных типов, которые вы должны использовать (как это часто бывает с контейнерами IOC). Используя отражение, вы можете создать экземпляр этого конкретного типа (через API отражения) и использовать его.

1 голос
/ 14 мая 2009

Я приведу вам пример.

В качестве упражнения по программированию я написал программу проверки mp3-файлов. Он сканирует мою музыкальную библиотеку и отображает теги id1 / id2, которые меня интересуют, в DataGridView. Я использую рефлексию, чтобы получить свойства из информационного класса mp3 без кода UI, который должен знать что-либо об этом классе. Если я хочу изменить отображаемую информацию, я могу либо отредактировать инфо-класс mp3, либо изменить его конфигурацию (в зависимости от того, как я написал класс), и не нужно обновлять пользовательский интерфейс.

Это также означало, что я смог использовать Dependency Injection, чтобы использовать его с конца для отображения информации о цифровых фотографиях, просто меняя класс библиотеки данных.

1 голос
/ 14 мая 2009

Отражение полезно для конфигурирования во время выполнения, позволяя управлять частями системы через внешнюю конфигурацию.

Например, фабрика классов может конструировать разные конкретные типы на основе входного файла, где конкретным типам требуется различная информация о конфигурации для вызова конкретного конструктора, а не использования интерфейса компоновщика. (Метод конструктора объекта, находящегося с помощью отражения).

...