Вызовите EqualityComparer.Default с Сесилом

Как сгенерировать необходимый IL-код для вызова метода System.Collections.Generic.EqualityComparer<T>.get_Default с помощью Mono Cecil?

Я пробовал подобные варианты этого, но получил различные ошибки, начиная от того, что PEVerify не может разрешить токен, из-за того, что Сесил жалуется, что что-то из другого модуля и его необходимо импортировать, до исключения ArgumentOutOfRangeException от самого Сесила.

Аргумент универсального типа исходит из PropertyType свойства, которое я здесь обрабатываю.

PropertyDefinition propertyDef = ...;
var equalityComparer = typeDef.Module.ImportReference(typeof(System.Collections.Generic.EqualityComparer<>));
var equalityComparerInst = equalityComparer.MakeGenericInstanceType(propDef.PropertyType);
var getDefaultMethod = equalityComparerInst.Resolve().Methods.First(m => m.Name == "get_Default");
var getDefaultMethodRef = typeDef.Module.ImportReference(getDefaultMethod, getDefaultMethod);
il.Append(il.Create(OpCodes.Call, getDefaultMethodRef));

Какой код мне для этого нужен?

Общие экземпляры из других модулей всегда сложны.


person ygoe    schedule 22.08.2016    source источник


Ответы (1)


Это должно работать

 private static void CallEqualityComparerDefault()
 {
     string assemblyPath = $"{Environment.CurrentDirectory}\\ClassLibrary1.dll";
     var mainModule = AssemblyDefinition.ReadAssembly(assemblyPath).MainModule;

     var methodDef = mainModule.Types.First(
         type => type.Name == "TestClass").Methods.Single(m => m.Name == "TestMethod");

     var eq = mainModule.Import(typeof(EqualityComparer<>));
     var obj = mainModule.Import(typeof(object));
     var genericEq = new GenericInstanceType(eq);
     genericEq.GenericArguments.Add(obj);
     var importedGenericEq = mainModule.Import(genericEq);
     var defaultMethodDef = importedGenericEq.Resolve().Methods.Single(m => m.Name == "get_Default");
     var methodRef =  mainModule.Import(defaultMethodDef);
     methodRef.DeclaringType = importedGenericEq;

     var ilProcessor = methodDef.Body.GetILProcessor();
     ilProcessor.InsertBefore(
         ilProcessor.Body.Instructions.First(), 
         Instruction.Create(OpCodes.Callvirt, methodRef));
     methodDef.Body.OptimizeMacros();

     mainModule.Write(assemblyPath + ".new.dll");
 }

ClassLibrary — это dll, которая содержит тип с именем TestClass, который содержит метод TestMethod.

Прежде чем добавить вызов EqualityComparer<>.Default, тело метода выглядит так:

IL_0000: nop
IL_0001: ret

И после:

IL_0000: callvirt class [mscorlib]System.Collections.Generic.EqualityComparer`1<!0> class [mscorlib]System.Collections.Generic.EqualityComparer`1<object>::get_Default()
IL_0005: nop
IL_0006: ret
person Dudi Keleti    schedule 22.08.2016
comment
Отлично, спасибо! Я почти пропустил строку methodRef.DeclaringType = importedGenericEq;, но, поскольку она все еще не работала для меня, я, наконец, нашел и эту строку. - person ygoe; 22.08.2016