Hi各位好, 如下是我的问题:
问题背景:
我想用Aop的方式对目标对象的方法做拦截, 并覆盖该方法, 对其做Task包装使原方法形成一个任务(Task)在独立的线程中执行, 覆盖后的方法返回Task或Task<xxx>, 下见代码:
被拦截的目标对象类:
public class TaskEntity
{
public TaskEntity()
{
Console.WriteLine("实例化'TaskEntity'对象");
}
//[JL_Intercept]
public virtual void TaskMethod()
{
for (int i = 0; i < 300; i++)
{
Console.WriteLine($"TaskMethod -> i = {i}");
}
}
[JL_Intercept]
public virtual string TaskMethod2()
{
for (int i = 0; i < 300; i++)
{
Console.WriteLine($"TaskMethod2 -> j = {i}");
}
return "完成";
}
}
希望被动态生成代码(下称 代理类)如下:
public class TaskModeExpand_No3 //: ITaskEntity
{
private StandardInterceptor Interceptor;
public TaskEntity SourceInstance { get; private set; }
public TaskModeExpand_No3(TaskEntity m_TaskEntity)
{
this.Interceptor = new StandardInterceptor();
this.SourceInstance = m_TaskEntity;
}
public Task TaskMethod()
{
var m_MethodInfo = this.SourceInstance.GetType().GetMethod("TaskMethod");
this.Interceptor.Before(this.SourceInstance, null);
Task m_Result = Task.Run(() =>
{
m_MethodInfo.Invoke(this.SourceInstance, null);
});
this.Interceptor.After(this.SourceInstance, null, null);
return m_Result;
}
///// <summary>
///// 测试方法2
///// </summary>
///// <returns></returns>
//public Task<string> TaskMethod2()
//{
// this.Interceptor.Before(this.SourceInstance, null);
// Task<string> m_Result = Task.Run<string>(() =>
// {
// return this.SourceInstance.TaskMethod2();
// });
// this.Interceptor.After(this.SourceInstance, null, null);
// return m_Result;
//}
}
其中定义的 public Task TaskMethod() 方法就是覆盖了TaskEntity中TaskMethod方法(此处熟悉AOP的朋友可以会说你这样覆盖不是标准C#语法上的覆盖, 是的 我这里没有用标准覆盖应为新方法的返回值统一被调整为Task或Task<xxx>, 已与原方法返回值不同也用不了标准覆盖), 并在原方法外包装了一个Task来达到目的.
根据代理类(class TaskModeExpand_No3)的代码使用ildasm工具对其还原IL编码见下图:
图1
图2
根据ildasm中展示的Pubilc Task TaskMethod()方法内容, 用 方式做如下代码还原:
var ilOfMethod = methodBuilder.GetILGenerator();
ilOfMethod.Emit(OpCodes.Newobj, nestedTypeCtor);
ilOfMethod.Emit(OpCodes.Stloc_0);
ilOfMethod.Emit(OpCodes.Ldloc_0);
ilOfMethod.Emit(OpCodes.Ldarg_0);
ilOfMethod.Emit(OpCodes.Stfld, m_FB_CThis);
ilOfMethod.Emit(OpCodes.Nop);
ilOfMethod.Emit(OpCodes.Ldloc_0);
ilOfMethod.Emit(OpCodes.Ldarg_0);
ilOfMethod.Emit(OpCodes.Call, propertyBeProxy.GetMethod);
ilOfMethod.Emit(OpCodes.Callvirt, typeof(object).GetMethod("GetType"));
ilOfMethod.Emit(OpCodes.Ldstr, m_MethodName);
ilOfMethod.Emit(OpCodes.Callvirt, typeof(Type).GetMethod("GetMethod", new Type[] { typeof(string) }));
ilOfMethod.Emit(OpCodes.Stfld, m_FB_MethodInfo);
ilOfMethod.Emit(OpCodes.Ldarg_0);
ilOfMethod.Emit(OpCodes.Ldfld, fieldInterceptor);
ilOfMethod.Emit(OpCodes.Ldarg_0);
ilOfMethod.Emit(OpCodes.Call, propertyBeProxy.GetMethod);
ilOfMethod.Emit(OpCodes.Ldnull);
ilOfMethod.Emit(OpCodes.Callvirt, InterceptType.GetMethod("Before"));
ilOfMethod.Emit(OpCodes.Nop);
ilOfMethod.Emit(OpCodes.Ldloc_0);
ilOfMethod.Emit(OpCodes.Ldftn, nestedTypeMethod);
ilOfMethod.Emit(OpCodes.Newobj, typeof(Action).GetConstructor(Type.EmptyTypes));
ilOfMethod.Emit(OpCodes.Call, typeof(Task).GetMethod("Run", new Type[] { typeof(Action) })); // BindingFlags.Public | BindingFlags.Static
ilOfMethod.Emit(OpCodes.Stloc_1);
ilOfMethod.Emit(OpCodes.Ldarg_0);
ilOfMethod.Emit(OpCodes.Ldfld, fieldInterceptor);
ilOfMethod.Emit(OpCodes.Ldarg_0);
ilOfMethod.Emit(OpCodes.Call, propertyBeProxy.GetMethod);
ilOfMethod.Emit(OpCodes.Ldnull);
ilOfMethod.Emit(OpCodes.Ldnull);
ilOfMethod.Emit(OpCodes.Callvirt, InterceptType.GetMethod("After"));
ilOfMethod.Emit(OpCodes.Nop);
ilOfMethod.Emit(OpCodes.Ldloc_1);
ilOfMethod.Emit(OpCodes.Stloc_2);
var methodresult = ilOfMethod.DeclareLocal(m_ReturnType);
ilOfMethod.Emit(OpCodes.Br_S, methodresult);
ilOfMethod.Emit(OpCodes.Ldloc_2);
ilOfMethod.Emit(OpCodes.Ret);
经反复验证, 问题只有一处 ilOfMethod.Emit(OpCodes.Newobj, typeof(Action).GetConstructor(Type.EmptyTypes)); 对应IL语句 IL_0043: newobj instance void [mscorlib]System.Action::.ctor(object, native int) , 此处因Action并没有构造函数的语法, 导致typeof(Action).GetConstructor(Type.EmptyTypes)返回为null, 但根据IL语句显示instance void [mscorlib]System.Action::.ctor(object, native int), 意思是此处需要实例化一个Aciton, 本人摸索良久未能实现, 恳请各路大神伸出援手, 愿奉上我全部的积分, 非常感谢!
补充, 拦截器类:
public class StandardInterceptor
{
public StandardInterceptor()
{
Console.WriteLine("实例化'StandardInterceptor'对象");
}
public void Before(object m_CurrObj, MethodInfo m_TagMethodInfo)
{
Console.WriteLine($"执行 {m_TagMethodInfo?.Name} 的 Before 方法 . . .");
}
public void After(object m_CurrObj, MethodInfo m_TagMethodInfo, Task<object> m_ReturnValue)
{
Console.WriteLine($"执行 {m_TagMethodInfo?.Name} 的 After 方法, 返回值{m_ReturnValue}");
}
}
你可以用 Castle 库的 DynamicProxy 动态拦截。
请教下大概怎么实现, 谢谢!
使用 IL.Emit(OpCodes.Newobj, typeof(Action).GetConstructors()[0]) 方式可以获得该构造函数实例.