前面已经实现了插件的自动加载,调用者拿到插件实例后,如果要调用含有Action或者Func<T>参数的方法时,需要思考"我这个回调应该在哪个域执行呢?"如下:
//应该是这样?
Debug.Print("Return => {0}",lastLoadedPlugin.Run(DateTime.Now.ToString(),
new Action(() => { Debug.Print(AppDomain.CurrentDomain.FriendlyName); })));
//还是应该这样
Debug.Print("Return => {0}",lastLoadedPlugin.Run(DateTime.Now.ToString(),
new Action(() => { Debug.Print(AppDomain.CurrentDomain.FriendlyName); }).CreateRemoteAppDomainProxy()));
得思考:CreateRemoteAppDomainProxy是不是应该被调用?
仔细想想,貌似应该在哪个域里执行,应该在接口定义时定义好以避免混乱。如下面定义一个接口:
public interface IPlugin
{
Guid PluginId { get; }
string Run(string args);
string Run(string args, [CallerDomainRun] Action action);
string Run(string args, [CallerDomainRun] Func<string> func);
}
"CallerDomainRun"表示此回调应该在调用者域里执行。
要达到上述目的,我们需要改动 Emit生成代理类的代理,依参数是不是具有 CallerDomainRun ,选择性的生成含有或者不含有"RemoteActionProxy.CreateProxyAction(action)"的代码。如下面所示生成样本:
public class XXXXXXX_Proxy : IPlugin
{
private Func<IPlugin> target;
public Guid PluginId
{
get { return target().PluginId; }
}
public string Run(string args)
{
return target().Run(args);
}
public string Run(string args, Action action)
{
return target().Run(args, RemoteActionProxy.CreateProxyAction(action));
}
public string Run(string args, Func<string> func)
{
return target().Run(args, RemoteFuncProxy<string>.CreateProxyFunc(func));
}
}
附:修改后的EMIT代码如下:
public static Type CreateFuncProxyType(Type targetType, Type baseType, bool igoreCrossDomainAttribute = false)
{
。。。
var methodBuilder = typeBuilder.DefineMethod(mi.Name, methodAttrs, CallingConventions.Standard, mi.ReturnType, parmTypes);
//方法体
{
var il = methodBuilder.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, fieldBuilder);
il.Emit(OpCodes.Callvirt, miInvoke);
if (parmTypes.Length > 0)
{
for (int i = 1; i <= parmTypes.Length; ++i)
{
switch (i)
{
case 0:
il.Emit(OpCodes.Ldarg_0);
break;
case 1:
il.Emit(OpCodes.Ldarg_1);
break;
case 2:
il.Emit(OpCodes.Ldarg_2);
break;
case 3:
il.Emit(OpCodes.Ldarg_3);
break;
default:
il.Emit(OpCodes.Ldarg_S, i);
break;
}
if (!igoreCrossDomainAttribute)
{
//检查参数,如果是 CrossDomain ,并且是Action 或者是 Func<T> 进行跨域替换
Type prmType = parmTypes[i - 1];
var prmInfo = parmInfos[i - 1];
if (prmInfo.GetCustomAttributes(typeof(CallerDomainRun), true).Length > 0)
{
if (prmType.Equals(typeof(Action)))
{
il.Emit(OpCodes.Call, miActionConverter);
}
else if (prmType.IsGenericType && (prmType.BaseType == typeof(Func<>).BaseType))
{
var miProxy = miFuncConverter.GetOrAdd(prmType, rt =>
{
var typeProxy = typeof(RemoteFuncProxy<>).MakeGenericType(rt.GetGenericArguments());
return typeProxy.GetMethod("CreateProxyFunc");
});
il.Emit(OpCodes.Call, miProxy);
}
}
}
}
}
il.Emit(OpCodes.Callvirt, mi);
if (mi.ReturnType == typeof(void))
{
il.Emit(OpCodes.Pop);
}
il.Emit(OpCodes.Ret);
}
map.Add(mi, methodBuilder);
});
。。。
}