Если отражение неэффективно, когда оно наиболее подходит?
Я нахожу много случаев, когда я думаю себе, что могу использовать relfection для решения проблемы, но обычно я этого не делаю, потому что я слышу много по строкам "не использую отражение, это слишком неэффективно",.
Теперь я нахожусь в ситуации, когда у меня есть проблема, когда я не могу найти другого решения, кроме как использовать отражение с new T()
, как указано в этот вопрос и ответ.
Итак, мне интересно, может ли кто-нибудь сказать мне конкретное конкретное использование рефлексии, и если есть набор рекомендаций, указывающих, когда это уместно и когда это не так?
Ответы
Ответ 1
Он часто "достаточно быстро", и если вам нужно быстрее (для жестких циклов и т.д.), вы можете выполнять метапрограммирование с помощью Expression
или ILGenerator
(возможно, через DynamicMethod
), чтобы сделать очень быстрый код ( включая некоторые трюки, которые вы не можете сделать в С#).
Отражение чаще используется для сценариев каркаса/библиотеки, где библиотека по определению ничего не знает о вызывающем, и должна работать на основе конфигурации, атрибутов или шаблонов.
Ответ 2
Если есть одна вещь, которую я ненавижу, когда она слышится "не использует рефлексию, она слишком неэффективна".
Слишком неэффективно для чего? Если вы пишете консольное приложение, которое запускается один раз в месяц и не критично для времени, действительно ли имеет значение, если вам требуется 30 секунд вместо 28, из-за того, что вы используете отражение?
Руководящие принципы, когда это нецелесообразно использовать, - это те, которые только вы можете собрать, поскольку они сильно зависят от того, что вы делаете, и насколько эффективны/эффективны альтернативы.
Ответ 3
Полезной абстракцией для эффективности кода является разбиение его на три категории времени, каждая на расстоянии порядка 3 порядков.
Сначала человеческое время. Там вы можете много сделать, когда вам нужно только держать человека в восторге от производительности вашего кода. Люди не могут понять разницу между кодом, которому требуется 10 миллисекунд или 20 миллисекунд, оба выглядят мгновенно. И человек прощает, когда программе требуется 6 секунд вместо 5, примерно 3 миллиарда машинных инструкций больше. Общими примерами программ, которые выполняются в человеческом времени, являются компиляторы и разработчики точек и щелчков. Использование отражения никогда не является проблемой.
Тогда есть время ввода/вывода. Когда ваша программа должна попасть на диск или в сеть. I/O медленный, ограничен механическим движением в случае диска, пропускной способностью и задержкой в случае сети. Вы всегда можете сказать, когда I/O является узким местом, ваша программа работает, но это не сильно повышает нагрузку на процессор. Операционная система постоянно блокирует поток, заставляя его ждать, пока запрос ввода-вывода не будет завершен.
Отражение работает при времени ввода-вывода. Чтобы получить данные типа, CLR должен прочитать метаданные сборки. И когда это не было сделано раньше, ваша программа вызовет ошибку страницы, требуя от операционной системы считывать данные с диска. Далее следует, что, грубо говоря, отражение может сделать связанный код ввода-вывода только в два раза медленнее. Обычно лучше, потому что после первого перфоманса метаданные кэшируются и могут быть получены быстрее. Отражение, таким образом, часто является приемлемым компромиссом. Канонические примеры представляют собой сериализацию и ORM с dbase.
Тогда есть машинное время. Исходная производительность ядра процессора колоссальна. Свойство getter может выполняться где-то между 0 и 1/2 наносекундой. Это не выгодно отличается от, скажем, PropertyInfo.GetValue(). Оба будут поддерживать загрузку процессора, вы увидите загрузку процессора для ядра на 100%. Но GetValue() стоит сотни, если не тысячи инструкций машинного кода. Не считая времени, необходимого для просмотра метаданных. Хотя это не так уж много, это быстро растет, когда вы зацикливаетесь.
Если вы не можете классифицировать свой код отражения в категориях времени человека или времени ввода-вывода, то отражение вряд ли будет подходящей заменой для обычного кода.
Ответ 4
Отражение не является неэффективным. Он менее эффективен, чем прямые вызовы. Поэтому personnaly я использую отражение, когда нет эквивалентного метода безопасного времени компиляции. ИМХО проблема с отражением - это не столько эффективность, сколько хрупкость кода, так как он использует магические строки, которые очень реорганизуются недружелюбно.
Ответ 5
Ключом к сохранению отражения от замедления вашей программы является не использование его внутри цикла. Если вы хотите прочитать свойство объекта во время запуска (происходит один раз), используйте отражение. Вы хотите прочитать свойство из списка из 10 000 объектов неизвестного типа, используйте рефлексию, чтобы получить делегат getter свойства однажды (поисковый запрос: PropertyInfo.GetGetMethod
), затем вызовите делегат 10 000 типов. В StackOverflow есть много примеров.
Ответ 6
Я использую его для архитектуры плагина - просматриваю сборки в папке плагина для методов, помеченных специальным атрибутом, указывающим информацию о плагине, и в рамках ведения журнала. Структура обнаруживает пользовательский атрибут самой сборки, который содержит информацию об авторе сборки, проекте, информации о версии и других тегах, которые регистрируются вместе со всем в трассировке стека.
Собираюсь отдать "коммерческую тайну", но это хороший. Структура позволяет пометить каждый метод или класс как "Story ref", например
[StoryRef(Ref="ImportCSV1")]
... и идея заключается в том, что он интегрируется в нашу гибкую структуру управления проектами: если бы были какие-то исключения, брошенные внутри этого класса/метода, метод ведения журнала использовал бы отражение для проверки атрибута StoryRef
в трассировке стека, и если это так, то будет зарегистрировано как исключение против этой истории. В программном обеспечении PM вы можете видеть исключения из Story (история похожа на экстремальный/гибкий вариант использования).
Я думаю, что действительное использование, по крайней мере! В основном, когда это просто кажется наиболее опрятным и соответствующим способом сделать это, я использую отражение. Ничто другое не приходит в это - я не могу придумать случая, по которому вы будете использовать размышления, чтобы сделать так много вызовов, что эффективность придет в него.
Ответ 7
Так что мне интересно, может ли кто-нибудь сказать я специально для размышления использования, и если есть набор рекомендации, указывающие, когда это подходит, а когда нет?
Плохой пример отражения - это из Википедии:
//Without reflection
Foo foo = new Foo();
foo.Hello();
//With reflection
Type t = Type.GetType("FooNamespace.Foo");
object foo = Activator.CreateInstance(t);
t.InvokeMember("Hello", BindingFlags.InvokeMethod, null, foo, null);
Здесь нет никакого преимущества в использовании отражения: код, не отражающий отражение, не только более эффективен, но и легче понимается.
Хорошее использование рефлексии - это такие вещи, как сериализация и объектно-реляционное сопоставление, которые легко реализовать, если у вас есть список свойств класса, но в противном случае требуется специально написанная функция для каждого класса.