Я ищу способ проверить во время выполнения, что переопределения 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.
Мои вопросы следующие:
- Чем объясняются различия в теле метода IL ?
- Как определить во время выполнения довольно надежным способом, делают ли реализации то же самое?
Обновление на основе c omments by thehenny :
Вызов из сборки, содержащей StaticClass.StaticMethod
, отличается тем, что использует MethodDef
(который, очевидно, может использоваться для вызова метода в том же сборка), тогда как внешние сборки используют MethodRef
.
Дополнительный вопрос: допустим, StaticClass
находится в пакете NuGet, и логика c для тестирования методов для конкретной реализации также присутствует там. , Теперь этому пакету требуется пример реализации для сравнения методов. Однако он не может определить это сам по себе, так как он будет отличаться (из-за MethodDef
). Он также не может ссылаться на подзависимость, потому что для доступа к StaticClass.StaticMethod
потребуется получить доступ к нему взамен, создавая циклическую ссылку. Если бы мы могли создать сборку во время выполнения, которая выдает результат MethodRef
, аналогичный результату жестко заданной реализации в отдельной сборке, это решило бы проблему. Как этого добиться? Или на самом деле, это третий байт, который отличается здесь, поэтому разница может быть в другой детали полностью. Что это?