Когда вы используете отражение? Паттерны / антипаттерны

Я понимаю API отражения (в C#), но не уверен, в какой ситуации я бы его использовал. Какие есть паттерны - антишаблоны для использования отражения?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
30
0
17 008
17
Перейти к ответу Данный вопрос помечен как решенный

Ответы 17

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

Я не знаю, шаблон ли это, но я использую отражение для генерации SQL из определений классов DAO.

Почему вы воссоздаете NHibernate? Просто используйте настоящую сделку.

yfeldblum 10.01.2009 04:33

Одна из причин в том, что мой DAL предшествует NHibernate. Я подумывал о переключении, но просто не хотел иметь дело с беспорядком конфигурации xml. Для моего использования то, что у меня есть, намного чище. Но спасибо за предложение.

Otávio Décio 10.01.2009 18:45

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

Ответ принят как подходящий

Единственное место, где я использовал Reflection в C#, было в фабричных шаблонах, где я создаю объекты (в моем случае, сетевые слушатели) на основе информации файла конфигурации. В файле конфигурации указано расположение сборок, имена типов в них и любые необходимые дополнительные аргументы. Фабрика подобрала этот материал и на его основе создала слушателей.

Я 2 года разрабатывал, не понимая цели рефлексии. У него очень нишевое применение, но он чрезвычайно эффективен, когда является правильным инструментом для работы.

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

В одном продукте, над которым я работаю, мы его часто используем, но Reflection - сложный и медленный зверь. Не ищите места, где можно его использовать, только потому, что это звучит весело или интересно. Вы будете использовать его, когда столкнетесь с проблемой, которую нельзя решить никаким другим способом (динамическая загрузка сборок для подключаемых модулей или фреймворков, проверка сборок, фабрики, типы которых не известны при сборке, и т. д.). Конечно, стоит взглянуть на учебники по отражению, чтобы увидеть, как это работает, но не попадайтесь в ловушку «иметь молоток и все, что похоже на гвоздь». У него очень специализированные варианты использования.

Я использую его в случае, аналогичном случаю, упомянутому Харпером Шелби, в котором файл конфигурации указывает во время выполнения, какой объект создать. В моем конкретном случае нет ничего более сложного, чем фабрика - только набор классов, реализующих общий интерфейс, и простая функция, которая читает файл конфигурации и создает соответствующий объект, возвращая интерфейс.

Я чаще всего использую его, когда мне нужно нарушить инкапсуляцию кого-то другого (обычно фреймворка). То есть мне нужно изменить какое-то частное поле, или вызвать частный метод, или создать экземпляр некоторого внутреннего класса в библиотеке, которую я не могу изменить. Хорошим примером является код от мой ответ до этот вопрос. В этом случае поведение рамочного метода ServiceBase.Run было неприемлемым, поэтому я использовал отражение, чтобы сделать то же самое, что и он, более приемлемым способом.

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

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

abelenky 10.01.2009 01:47

Напротив. Обычно я делаю это правильно, потому что они сделали это неправильно.

P Daddy 10.01.2009 01:49

но он все еще сломан, как ни крути :)

gbjbaanb 10.01.2009 03:49

И, к счастью, у нас есть отражение, позволяющее исправить это. Не обманывайте себя, думая, что вам никогда не придется иметь дело с чужим неработающим кодом. В любом случае, автор библиотеки никогда не может предвидеть всех вариантов использования своего кода. С инкапсуляцией это усугубляется.

P Daddy 10.01.2009 04:21

Если вы не планируете обновлять фреймворк, который вы «взламываете» с помощью отражения, это может быть нормально. Однако после того, как вы выполните обновление, все может сломаться, поскольку все частное может быть изменено из-за рефакторинга или оптимизации.

LeBodro 05.04.2017 01:33

Да, конечно, есть риск, что в следующей версии что-то сломается. Это риск, независимо от того, используете ли вы отражение, пусть и меньшее, если вы этого не сделаете, и которое легче найти обычно. Поэтому, безусловно, желательно тщательно протестировать свой код отражения, особенно при изменении версий кода, которые вы, как вы выразились, «взламываете». И, конечно же, эту технику следует использовать экономно и осторожно. Тем не менее, на данный момент я не могу вспомнить время, когда это вызывало проблему в каком-либо из моих проектов.

P Daddy 06.04.2017 14:03

Также стоит упомянуть, что риски отказа во время выполнения из-за использования отражения не являются уникальными для случая использования, который я описываю здесь.

P Daddy 06.04.2017 14:03

Я довольно часто использую отражение в своих модульных тестах, особенно когда проверяю анонимные типы. Я также использовал его как способ легко клонировать / копировать объекты модели. Вместо того, чтобы писать код для этого для каждого объекта модели, я могу легко создать объект определенного типа с помощью отражения, опросить общедоступные свойства входящего объекта и вызвать установщики для соответствующих свойств клонированных объектов. Я также использую его с классами, созданными дизайнером, которые реализуют те же сигнатуры методов, но не имеют связанного интерфейса. В таких случаях я могу опросить объект, чтобы узнать, есть ли у него требуемый метод, и вызвать его, если он есть. Сущности LINQ2SQL похожи на это, поэтому в моем методе OnSubmit оболочки контекста поддельных данных я использую отражение, чтобы получить метод OnValidate и вызвать его для модульного тестирования.

Я использую его в двоичном сериализаторе (protobuf-net). Я использую отражение только для построения модели - когда она используется (например, во время [де] сериализации), она использует делегаты и т. д. Для максимальной производительности.

Я также использовал его (вместе с ComponentModel и Reflection.Emit) в HyperDescriptor для создания ускоренного доступа к свойствам (примерно в 100 раз быстрее обычного отражения).

И по необходимости вам нужно использовать отражение, если вы создание собственного Expression.

+1 об использовании заводского паттерна - там очень мощно.

Помимо заводского шаблона, каждый раз, когда я его использовал, мне, наверное, не стоило ...

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

Анти-шаблон - использовать его для доступа к свойствам, которые разработчики классов пометили как частные, не зная, почему они пометили их как частные. Я сделал это с помощью элемента управления DataGridView WinForms, чтобы отключить логическую переменную, чтобы я мог перемещать «сопутствующий» столбец при перемещении его дополнения. Опять же, это очень круто, но этот код ужасно выйдет из строя, если в новом выпуске изменится эта частная собственность (вполне возможно, что она исчезнет в версии 3.0 или 3.5 ...).

Я считаю, что отражение (в сочетании с загрузкой класса времени выполнения) необходимо для реализации плагинов:

  1. поиск банок / сборок в известном месте
  2. перечислить jar-файлы / сборки для классов, поддерживающих интерфейс, поддерживаемый вашим плагином
  3. создать экземпляр плагина во время выполнения

Отражение позволяет решать проблемы, которые в противном случае потребовали бы дублирования кода. Если вы обнаружите, что копируете и вставляете код, и не видите, как объектно-ориентированный шаблон может помочь, возможно, если вы можете вызвать их, тогда «команда r» может помочь.

Просто шучу. Я целую вечность не мог понять, в чем дело. Если вы можете написать код для выполнения работы, зачем вам вообще нужно использовать отражение?

Скажем, у вас есть много объектов графического интерфейса, таких как формы или объекты «таблицы». Он привязывается к бизнес-объекту, например к Customer:

public class Customer
{
   public string FirstName;
   public string LastName;

}

Ваши пользователи не хотят видеть заголовки столбцов «Имя» или «Фамилия». Они хотят «христианское имя» и фамилию. Вы не хотите кодировать буквальные строки во всех ваших объектах GUI на случай, если они передумают «Имя» и «Фамилия отца» (я знаю, дерьмовый пример).

Если вы определяете свой класс с помощью атрибутов:

public class Customer
{
   [Description("Christian Name")]
   public string FirstName;

   [Description("Surname")]
   public string LastName;

}

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

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

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

user93202 21.09.2009 18:52

ИМХО ваш пример - антипаттерн. Использование здесь атрибута Description сделало бы обработку i18n практически невозможной. Вам нужно будет переводить не только формы, но и код.

Nux 27.10.2011 17:27

Я использовал отражение во многих местах. Основные широкие категории включают:

  1. Автоматически сгенерированные графические интерфейсы (т.е. редактор свойств). Вы можете перебирать свойства объекта и использовать реестр фабрик элементов пользовательского интерфейса для создания формы. Я использую атрибуты свойств для создания пользовательского интерфейса.
  2. Сериализация. Я написал фреймворки сериализации, использующие отражение для сериализации и десериализации объектов.
  3. Веб-сервисы. Подобно сериализации, я использовал отражение для создания и использования сообщений SOAP, а также для генерации WSDL.
  4. Языки, специфичные для домена. Интерпретируемые языки сценариев обычно привязываются к объектам и методам с помощью отражения.
  5. Инструменты отладки. Такие инструменты могут использовать отражение для проверки состояния объекта. Удобно для создания сообщений журнала в условиях сбоя.

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

Использование рефлексии - это мощное средство, но оно не сделает вас более популярным на вечеринках. Используйте его только там, где сцепление намеренно слабое. Настолько слабый, что можно ожидать, что он сломается во время выполнения. Отличный пример - привязка данных в WPF.

Я не уверен насчет антипаттернов, но наверняка это будет относиться к тому, что нужно делать во время выполнения во время компиляции ...

Лучшее место, где я использую отражение: извлечение типа из базы данных.

У меня есть класс, которому нужно знать, какую библиотеку вызывать. Имейте в виду, что по мере добавления новых инструментов в список классу необходимо распознавать новые инструменты без перекомпиляции, поэтому об операторе switch не может быть и речи.

Вместо этого я сохраняю строку отражения в базе данных, которая сообщает классу «создать один из этих ...». Поскольку я (программист) всегда гарантирую, что класс является производным от одного базового класса, идея работает. Это чисто и эффективно.

Но я согласен с тем, что если вы используете отражение не только для этих сценариев «автоматически сгенерированного кода», то вы можете открыть для себя мир боли, когда дело доходит до поддержки кода в будущем.

(входит голос мудрого старого мудреца)
Отражение обладает невероятной силой ... используйте силу с мудростью.

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

Лично я не использую много. Я обычно использую его, когда у меня нет другого способа что-то кодировать.

Вот простой пример:

public static void LogException(Exception exc, string source)
{
    try
    {
        // Logic for logging exceptions
    }
    catch(Exception ex)
    {
        //If logging fails  for some reason 
        //then we still log it using reflection
        LogException(ex, "Error while logging an exception");
    }
}

Другие вопросы по теме