Alternatives of CompileToMethod in .Net Standard

2019-04-19 08:58发布

问题:

I'm now porting some library that uses expressions to .Net Core application and encountered a problem that all my logic is based on LambdaExpression.CompileToMethod which is simply missing in. Here is sample code:

public static MethodInfo CompileToInstanceMethod(this LambdaExpression expression, TypeBuilder tb, string methodName, MethodAttributes attributes)
{
    ...

    var method = tb.DefineMethod($"<{proxy.Name}>__StaticProxy", MethodAttributes.Private | MethodAttributes.Static, proxy.ReturnType, paramTypes);
    expression.CompileToMethod(method);

    ...
}

Is it possible to rewrite it somehow to make it possible to generate methods using Expressions? I already can do it with Emit but it's quite complex and i'd like to avoid it in favor of high-level Expressions.

I tried to use var method = expression.Compile().GetMethodInfo(); but in this case I get an error:

System.InvalidOperationException : Unable to import a global method or field from a different module.

I know that I can emit IL manually, but I need exactly convert Expression -> to MethodInfo binded to specific TypeBuilder instead of building myself DynamicMethod on it.

回答1:

I ran into the same issue when porting some code to netstandard. My solution was to compile the lambda to a Func using the Compile method, store the Func in a static field that I added to my dynamic type, then in my dynamic method I simply load and call the Func from that static field. This allows me to create the lambda using the LINQ Expression APIs instead of reflection emit (which would have been painful), but still have my dynamic type implement an interface (which was another requirement for my scenario).

Feels like a bit of a hack, but it works, and is probably easier than trying to recreate the CompileToMethod functionality via LambdaCompiler.



回答2:

It is not an ideal solution but it is worth considering if you don't want to write everything from the scratch:

  1. If you look on CompileToMethod implementation, you will see that under the hood it uses internal LambdaCompiler class.
  2. If you dig even deeper, you willl see that LambdaCompiler uses System.Reflection.Emit to convert lambdas into MethodInfo.
  3. System.Reflection.Emit is supported by .NET Core.
  4. Taking this into account, my proposition is to try to reuse the LambdaCompiler source code. You can find it here.

The biggest problem with this solution is that:

  1. LambdaCompiler is spread among many files so it may be cumbersome to find what is needed to compile it.
  2. LambdaCompiler may use some API which is not supported by .NET Core at all.

A few additional comments:

  1. If you want to check which API is supported by which platform use .NET API Catalog.
  2. If you want to see differences between .NET standard versions use this site.