Одинаковое тело метода, другой результат GetILAsByteArray - PullRequest
0 голосов
/ 05 апреля 2020

Я ищу способ проверить во время выполнения, что переопределения GetHashCode не делают ничего, кроме простого вызова определенного метода stati c. Я близок, но есть небольшие различия, которые я пока не могу объяснить.

На MethodInfo объектах рассматриваемых переопределений, которые я называю GetMethodBody().GetILAsByteArray(). Идея состоит в том, чтобы сравнить эти реализации с набором вероятных кандидатов. Переопределение GetHashCode, которое просто вызывает наш метод stati c, имеет тенденцию выглядеть как одна из трех возможностей:

// Option 1 - Expression-bodied
public override int GetHashCode() => StaticClass.StaticMethod();
// Option 2 - Method-bodied
public override int GetHashCode()
{
    return StaticClass.StaticMethod();
}
// Option 3 - Method-bodied with intermediate var
public override int GetHashCode()
{
    var hashCode = StaticClass.StaticMethod();
    return hashCode;
}

Для простоты мы рассмотрим только вариант 1, реализацию с выражением тела.

Я уже установил, что следующее не влияет на результат :

  • Пространство имен
  • Модификатор доступа
  • Тип вложенности (т.е. типы, вложенные в другой тип)
  • Тип уплотнения (public [sealed] class)
  • Метод уплотнения (public [sealed] override int GetHashCode)

Тест Решение содержит консольное приложение, библиотеку классов (которая также предоставляет StaticClass.StaticMethod) и библиотеку второго класса. (Все ссылается на первую библиотеку классов, а консольное приложение также ссылается на библиотеку второго класса.)

Я сравниваю идентичные переопределенные выражения GetHashCode, определенные следующими классами :

  • Lib (класс, который находится в той же библиотеке классов, ответственной за StaticClass.StaticMethod)
  • OtherLib (класс, который живет в другой библиотеке классов)
  • ConsoleApplication (класс, который находится в консольном приложении)
  • LibGenerated (класс, сгенерированный во время выполнения первой библиотекой классов, в новую сборку AssemblyA).
  • Lib2Generated (класс, сгенерированный во время выполнения другой библиотекой классов, в новую сборку AssemblyB).
  • ConsoleApplicationGenerated (класс, сгенерированный во время выполнения консольным приложением, в новую сборку AssemblyC).

Вот байтовые массивы соответствующих реализаций в режиме отладки:

Lib:                            02|40|19|00|00|06|42
OtherLib:                       02|40|13|00|00|10|42
ConsoleApplication:             02|40|14|00|00|10|42
LibGenerated:                   02|40|01|00|00|10|42
Lib2Generated:                  02|40|01|00|00|10|42
ConsoleApplicationGenerated:    02|40|01|00|00|10|42

Здесь они находятся в режиме выпуска (где единственное отличие состоит в том, что ConsoleApplication теперь соответствует OtherLib):

* 1 070 *

Рассматривая результаты, мы можем отметить следующее:

  • Жестко закодированный тип в той же сборке, что и StaticClass.StaticMethod, является единственным, у которого есть другой второй к последнему байту .
  • В режиме отладки третий байт отличается для каждого из жестко-закодированных типов, а также отличается от сгенерированных типов.
  • В режиме выпуска третий байт отличается для сборка, содержащая StaticClass.StaticMethod, в то время как другие жестко запрограммированные реализации имеют точно такие же тела метода.
  • Сгенерированные типы имеют точно такие же тела метода (даже если они тоже живут в разных сборках с разными именами модулей) .

Естественно, здесь наиболее интересен режим Release.

Мои вопросы следующие:

  1. Чем объясняются различия в теле метода IL ?
  2. Как определить во время выполнения довольно надежным способом, делают ли реализации то же самое?

Обновление на основе c omments by thehenny :

Вызов из сборки, содержащей StaticClass.StaticMethod, отличается тем, что использует MethodDef (который, очевидно, может использоваться для вызова метода в том же сборка), тогда как внешние сборки используют MethodRef.

Дополнительный вопрос: допустим, StaticClass находится в пакете NuGet, и логика c для тестирования методов для конкретной реализации также присутствует там. , Теперь этому пакету требуется пример реализации для сравнения методов. Однако он не может определить это сам по себе, так как он будет отличаться (из-за MethodDef). Он также не может ссылаться на подзависимость, потому что для доступа к StaticClass.StaticMethod потребуется получить доступ к нему взамен, создавая циклическую ссылку. Если бы мы могли создать сборку во время выполнения, которая выдает результат MethodRef, аналогичный результату жестко заданной реализации в отдельной сборке, это решило бы проблему. Как этого добиться? Или на самом деле, это третий байт, который отличается здесь, поэтому разница может быть в другой детали полностью. Что это?

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