试图引用Task.ContinueWith<从Mono.Cecil

本文关键字:Mono Cecil ContinueWith 引用 Task | 更新日期: 2023-09-27 17:52:52

问题

我试图得到这个IL指令严格使用Mono.Cecil。现在,我找到的唯一解决方案涉及导入每个引用程序集及其导出类型,调用MakeGenericInstanceType()Module.Import()

需要IL指令

生成的代码是:

call instance class [mscorlib]System.Threading.Tasks.Task`1<!!0> class [mscorlib]System.Threading.Tasks.Task`1<string>::ContinueWith<class MyClass`1<valuetype [mscorlib]System.DateTime>>(class [mscorlib]System.Func`2<class [mscorlib]System.Threading.Tasks.Task`1<!0>, !!0>)

我能得到的(仅使用引用)

我试图做同样的事情没有导入一切(因此只使用引用),但不能这样做。我做过的最接近的两件事是:

测试1

call instance class [mscorlib]System.Threading.Tasks.Task`1<!1> class [mscorlib]System.Threading.Tasks.Task`1<string>::ContinueWith<class MyClass`1<valuetype [mscorlib]System.DateTime>>(class [mscorlib]System.Func`2<class [mscorlib]System.Threading.Tasks.Task`1<!0>, !1>)

问题: !!0!1取代

测试2

call instance class [mscorlib]System.Threading.Tasks.Task`1<class MyClass`1<valuetype [mscorlib]System.DateTime>> class [mscorlib]System.Threading.Tasks.Task`1<string>::ContinueWith<class MyClass`1<valuetype [mscorlib]System.DateTime>>(class [mscorlib]System.Func`2<class [mscorlib]System.Threading.Tasks.Task`1<string>, class MyClass`1<valuetype [mscorlib]System.DateTime>>)

问题:所有显式定义的泛型:

  • !0 => string
  • !!0 => class MyClass`1<valuetype [mscorlib]System.DateTime>

知道如何得到想要的指令,只使用引用吗?

成功的(但非常黑客的)代码

var task = module.GetEverySingleType().First(...);
var returnType = module.GetEverySingleType().First(...);
var continueWith = module.Import((from m in task.GetMethods()
                                  where m.Name == "ContinueWith"
                                  let p = m.GetParameters()
                                  where p.Length == 1 && p[0].ParameterType.Name == "Func`2"
                                  select m.MakeGenericMethod(returnType)).First());
Instruction.Create(OpCodes.Call, continueWith);

GetEverySingleType()导入每个程序集,将其装入内存,并枚举其类型。

不成功的代码(测试2)

var func = Module.Import(typeof(Func<,>)).MakeGenericType(taskType, returnType);
var continueWith = new GenericInstanceMethod(new MethodReference("ContinueWith", Module.Import(typeof(Task<>)).MakeGenericInstanceType(returnType), taskType) { HasThis = true });
continueWith.GenericArguments.Add(returnType);
continueWith.Parameters.Add(new ParameterDefinition(Module.Import(typeof(Func<,>)).MakeGenericType(taskType, returnType)));
Instruction.Create(OpCodes.Call, continueWith);

任何想法?

试图引用Task.ContinueWith<从Mono.Cecil

在第二个示例中continueWith中没有任何泛型参数,这就是为什么结果方法引用不使用任何参数的原因。您的目标方法签名使用两个,!0!!0。必须保留泛型参数,并且返回/参数类型必须使用它们。你似乎没有问题从系统程序集导入东西,所以试试这个:

var bf = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly ;
// import the *generic method* from the *generic type*
// can be done once per module
var cwgg = module.Import (typeof (Task<>).GetMethods (bf).Where (_ =>
  _.Name == "ContinueWith"    &&
  _.IsGenericMethodDefinition &&
  _.GetParameters ().Length == 1).Single ()) ;
// close Task<>, keep ContinueWith generic parameter open
var cwgi     = new MethodReference (cwgg.Name, cwgg.ReturnType, taskType) ;
cwgi.HasThis = true ;
cwgi.GenericParameters.Add (new GenericParameter    ("TNewResult", cwgi)) ;
cwgi.Parameters.Add        (new ParameterDefinition (
    cwgg.Parameters[0].ParameterType)) ;
// close ContinueWith
var continueWith = new GenericInstanceMethod (cwgi) ;
continueWith.GenericArguments.Add      (returnType) ;