Есть ли в .NET эквивалент JavaF ClassFileTransformer? (способ заменить класс) - PullRequest
4 голосов
/ 11 апреля 2010

Я искал это довольно долго, но безуспешно. Есть ли эквивалент в Java ClassFileTransformer в .NET? По сути, я хочу создать класс CustomClassFileTransformer (который в Java будет реализовывать интерфейс ClassFileTransformer), который вызывается при загрузке класса, и ему разрешается настраивать его и заменять его настроенной версией.

Я знаю, что есть фреймворки, которые делают подобные вещи, но я искал что-то более простое, например, реализацию собственной ClassFileTransformer. Возможно ли это?


РЕДАКТИРОВАТЬ # 1 . Подробнее о том, зачем мне это нужно:

По сути, у меня есть приложение на C #, и мне нужно отслеживать инструкции, которые оно хочет выполнить, чтобы обнаружить операции чтения или записи в поля (операции Ldfld и Stfld) и вставьте несколько инструкций до начала чтения / записи.

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

  1. Получите метод MethodBody, используя MethodBase.GetMethodBody()
  2. Преобразуйте его в байтовый массив с помощью MethodBody.GetILAsByteArray(). Возвращаемое byte[] содержит байт-код.
  3. Анализировать байт-код, как объяснено здесь , возможно, вставляя новые инструкции или удаляя / изменяя существующие, изменяя содержимое массива.
  4. Создайте новый метод и используйте новый байт-код для создания его тела, с MethodBuilder.CreateMethodBody(byte[] il, int count), где il - массив с байт-кодом. Я поместил все эти измененные методы в новый класс и использую новый класс, чтобы заменить тот, который первоначально должен был быть загружен.

Альтернативой замене классов было бы как-то получать уведомление всякий раз, когда вызывается метод. Затем я заменил бы вызов этого метода на вызов моего собственного настроенного метода, который я бы настраивал только при первом вызове, а затем помещал бы его в словарь для будущего использования, чтобы уменьшить накладные расходы (для будущих вызовов Я просто посмотрю на метод и вызову его; мне больше не нужно анализировать байт-код). В настоящее время я изучаю способы сделать это, и LinFu выглядит довольно интересно, но если бы было что-то вроде ClassFileTransformer, это было бы намного проще: я просто переписал класс, заменил его и позволил коду работать без контроля ничего.

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


РЕДАКТИРОВАТЬ # 2 . Почему мне нужно сделать это во время выполнения.

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


ВОЗМОЖНОЕ (НО ДОВОЛЬНО ХАРДКОР) РЕШЕНИЕ . В случае, если кому-то интересно (и я вижу, что вопрос был задуман, так что, я думаю, кто-то есть), это - это то, на что я сейчас смотрю. По сути, мне нужно было реализовать API профилирования, и я буду регистрироваться на интересующие меня события, в моем случае, когда начинается компиляция JIT. Выдержка из поста блога:

  • В обратном вызове ICorProfilerCallback2 :: ModuleLoadFinished вы вызываете ICorProfilerInfo2 :: GetModuleMetadata, чтобы получить указатель на интерфейс метаданных в этом модуле.
  • QI для интерфейса метаданных, который вы хотите. Найдите в MSDN «IMetaDataImport» и пролистайте оглавление, чтобы найти темы по интерфейсам метаданных.
  • Как только вы окажетесь в стране метаданных, у вас будет доступ ко всем типам в модуле, включая их поля и прототипы функций. Вам может понадобиться проанализировать подписи метаданных, и вам может пригодиться этот анализатор подписей.
  • В вашем обратном вызове ICorProfilerCallback2 :: JITCompilationStarted вы можете использовать ICorProfilerInfo2 :: GetILFunctionBody для проверки исходного IL, а ICorProfilerInfo2 :: GetILFunctionBodyAllocator, а затем ICorProfilerInfo2 :: SetILFunctionBody заменить этот IL своим.

Хорошие новости: я получаю уведомление, когда начинается JIT-компиляция, и я могу прямо сейчас заменить байт-код, не беспокоясь о замене класса и т. Д. Не очень хорошие новости: вы не можете вызывать управляемый код из Методы обратного вызова API, что имеет смысл, но означает, что я сам разбираю код IL и т. Д., В отличие от возможности использовать Сесил, что было бы очень просто.

Я не думаю, что есть более простой способ сделать это без использования AOP-фреймворков (например, PostSharp ). Если у кого-то есть другая идея, пожалуйста, дайте мне знать. Я не отмечаю вопрос как ответ.

Ответы [ 2 ]

1 голос
/ 01 июля 2010

После некоторых исследований я отвечаю на свой собственный вопрос: в 100% * .NET нет эквивалента или какого-либо простого способа замены классов.

Можно получить контроль над процессом загрузки классов, размещая CLR, но это довольно низкоуровневый уровень, с ним нужно быть осторожным, и это невозможно в каждом сценарии. Например, если вы работаете на сервере, у вас могут не быть прав на размещение CLR. Кроме того, если вы используете приложение ASP.NET, вы не сможете сделать это, потому что ASP.NET уже предоставляет хост CLR.

Жаль. NET не поддерживает это; им было бы так легко это сделать, они просто должны уведомить вас перед загрузкой класса и дать вам возможность изменить класс перед передачей его в CLR для его загрузки.

1 голос
/ 11 апреля 2010

Я не знаю прямого эквивалента в .NET для этого.

Однако есть несколько способов реализовать аналогичную функциональность, например, используя Reflection.Emit для генерации сборок и типов по запросу, используя RealProxy для создания прокси-объектов для интерфейсов и MarshalByRefObject объектов. Однако, чтобы посоветовать, что использовать, важно знать больше о фактическом случае использования.

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