Если рефлексия неэффективна, когда она наиболее уместна? - PullRequest
16 голосов
/ 31 июля 2010

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

Теперь у меня возникла проблема, когда я не могу найти другого решения, кроме использования отражения с new T(), как описано в в этом вопросе и ответе .

Так что мне интересно, может ли кто-нибудь сказать мне конкретное предполагаемое использование отражения, и есть ли набор рекомендаций, чтобы указать, когда это уместно, а когда нет?

Ответы [ 7 ]

24 голосов
/ 31 июля 2010

Это часто "достаточно быстро", и если вам нужно быстрее (для коротких циклов и т. Д.), Вы можете выполнять метапрограммирование с Expression или ILGenerator (возможно, через DynamicMethod), чтобы сделать чрезвычайно быстрый код (включая некоторые трюки, которые вы не можете сделать в C #).

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

21 голосов
/ 31 июля 2010

Если есть одна вещь, которую я ненавижу , слыша, что это "не используйте отражение, это слишком неэффективно".Если вы пишете консольное приложение, которое запускается раз в месяц и не критично ко времени, действительно ли имеет значение, если оно занимает 30 секунд вместо 28, потому что вы используете отражение?* Указания на то, когда нецелесообразно использовать, - это те, которые действительно можно собрать, поскольку они сильно зависят от того, что вы делаете, и от того, насколько эффективны / эффективны альтернативы.

20 голосов
/ 31 июля 2010

Полезная абстракция для эффективности кода состоит в том, чтобы разделить его на три категории времени, каждая на расстоянии порядка 3 порядков.

Первый - это время человека.Вы можете многое сделать, если вам нужно только, чтобы человек был доволен производительностью вашего кода.Люди не могут почувствовать разницу между кодом, который требует 10 миллисекунд или 20 миллисекунд, оба выглядят мгновенно.И человек прощает, когда программе нужно 6 секунд вместо 5, что примерно на 3 миллиарда машинных инструкций больше.Типичными примерами программ, запускаемых человеком, являются компиляторы и дизайнеры «укажи и щелкни».Использование отражения никогда не является проблемой.

Тогда есть время ввода-вывода.Когда ваша программа должна попасть на диск или в сеть.Ввод / вывод медленный, ограничен механическим движением в случае диска, пропускной способностью и задержкой в ​​случае сети.Вы всегда можете сказать, когда узким местом является I / O, ваша программа работает, но не сильно увеличивает нагрузку на процессор.Операционная система постоянно блокирует поток, заставляя его ждать завершения запроса ввода-вывода.

Отражение работает во время ввода-вывода.Чтобы получить данные типа, CLR должен прочитать метаданные сборки.А когда этого не было сделано раньше, ваша программа вызовет сбой страницы, требующий от операционной системы считывания данных с диска.Далее следует, что, примерно, отражение может сделать код, связанный с вводом / выводом, только в два раза медленнее.Обычно лучше, потому что после первого попадания метаданные кэшируются и могут быть получены намного быстрее.Поэтому отражение часто является приемлемым компромиссом.Каноническими примерами являются сериализация и базы данных ORM.

Тогда есть время машины.Необработанная производительность ядра ЦП огромна.Получатель свойства может выполняться где-то между 0 и 1/2 a наносекунда .Это не выгодно отличается, скажем, от PropertyInfo.GetValue ().Оба будут загружать процессор, вы увидите, что загрузка процессора для ядра составляет 100%.Но GetValue () стоит сотни, если не тысячи инструкций машинного кода.Не считая времени, необходимого для страницы в метаданных.Несмотря на то, что это время не является инкрементным, оно быстро растет при цикле.

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

8 голосов
/ 31 июля 2010

Ключ к замедлению отражения от программы - не использовать его внутри цикла.Если вы хотите прочитать свойство объекта во время запуска (происходит один раз), используйте отражение.Вы хотите прочитать свойство из списка 10000 объектов неизвестного типа, использовать отражение, чтобы получить делегат получателя свойства один раз (поисковый запрос: PropertyInfo.GetGetMethod), а затем вызвать делегат 10 000 типов.Есть множество примеров этого в StackOverflow.

7 голосов
/ 31 июля 2010

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

5 голосов
/ 31 июля 2010

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

Собираюсь выдать «коммерческую тайну», но она хорошая. Фреймворк позволяет пометить каждый метод или класс с помощью 'Story ref', например,

[StoryRef(Ref="ImportCSV1")]

... и идея заключается в том, что он интегрируется в нашу гибкую инфраструктуру управления проектами: если бы в этом классе / методе были сгенерированы какие-либо исключения, метод ведения журнала использовал бы отражение, чтобы проверить атрибут StoryRef в трассировке стека и если так, то это будет зарегистрировано как исключение против этой истории. В программном обеспечении PM вы можете видеть исключения от Story (история похожа на случай экстремального / гибкого использования).

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

2 голосов
/ 31 июля 2010

Так что мне интересно, кто-нибудь может сказать мне конкретное предполагаемое использование отражения, и есть ли набор рекомендаций, чтобы указать, когда это уместно, а когда нет?

A плохой пример отражения - вот этот из Википедии :

//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);

Здесь нет никакого преимущества использования отражения: код, не использующий отражение, не являетсятолько более эффективно, но проще для понимания.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...