c#中,可以使用Emit动态生成IL代码,其主要是为了解决:
- 对方法的动态扩展
- 优化反射速度
但是由于Emit的使用需要大量操作Opcodes,可能难以理解,因此可以使用一些NuGet包来辅助进行Emit操作。
使用
下载NuGet包:DynEmit。此包尚处于PreRelease状态,需要设置NuGet可见PreRelease。
基本使用
例如,我们需要获得Message类的PayLoad字段:
以下为Message定义:
public class Message
{
public string Payload { get; } = "TestContent";
}
对于正常使用Emit来获取该字段,需要这样操作:
DynamicMethod methods = new DynamicMethod("method",typeof(string),new []{typeof(Message)});
var il = methods.GetILGenerator();
il.Emit(OpCodes.Ldarg,0);
il.Emit(OpCodes.Callvirt,typeof(Message).GetProperties()[0].GetMethod!);
il.Emit(OpCodes.Ret);
Delegate @delegate = methods.CreateDelegate(typeof(Func<Message, string>));
Console.WriteLine(@delegate.DynamicInvoke(new Message()));
但是在DynEmit下,我们可以避免使用OpCodes,而使用DynEmit封装好的方法:
DynEmitMethod<Func<Message,string>> method = new ("method");
var argument = method.LoadArgument(0);
var obj = argument.CastToLocalVariable(typeof(object));
var res = method.ActionNonStaticMethod(typeof(Message).GetProperties()[0].GetMethod!,(DynObject)obj,new())!;
res.PushValue();
method.Invoke(new Message());
缺点
由于DynEmit内部对OpCodes进行了封装,因此可能会出现一些冗余类,可能会比直接调用Emit要慢。