通过这种方式可以直接对接口实例化了(伪),动态的通过委托来实现接口,匿名类型是在运行时动态构建的。
实现思路:
使用AssemblyBuilder、TypeBuilder构建类型,并用FieldBuilder构建一个字典字段<String, Delegate>,用来保存函数名和匿名函数,MethodBuilder构建方法并使用IL流填充,调用保存的匿名函数表中的函数。
构建动态类型的方法要求传入一个<String, Delegate>函数字典,可以使用字典的初始化器来初始化匿名函数来达到实现接口的匿名类 。
因为没有先天的支持,不像Java中可以直接用关键字和标识符来声明,只能用字符串来标示对应的函数。
演示:
public class Program { public interface IMyInterface { string Get(string p); void Hello(); } public static void Main() { IMyInterface impl = DynamicType.New<IMyInterface>(new Dictionary<string, Delegate>() { { "Get", new Func<string, string>((sp)=> { return "Get..." + sp; }) }, { "Hello", new Action(()=> { Console.WriteLine("Hello..."); }) } }); Console.WriteLine(impl.Get("Func")); impl.Hello(); } }
代码:
using System; using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; namespace Jxcode.Common { public class DynamicType { public static T New<T>(Dictionary<string, Delegate> methods) { Type t = typeof(T); AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly( name: new AssemblyName("DynamicAssembly." + typeof(T).FullName), access: AssemblyBuilderAccess.Run); ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicModule"); TypeBuilder typeBuilder = moduleBuilder.DefineType( name: "new" + t.Name, attr: TypeAttributes.Class | TypeAttributes.Public, parent: null, interfaces: new Type[] { t }); //把匿名方法表存起来 FieldBuilder fieldBuilder = typeBuilder.DefineField( fieldName: "_methodTable", type: typeof(Dictionary<string, Delegate>), attributes: FieldAttributes.Private); MethodInfo[] methodInfos = t.GetMethods(); for (int i = 0; i < methodInfos.Length; i++) { MethodInfo methodInfo = methodInfos[i]; Type[] paramTypes = ParamInfoToTypes(methodInfo); MethodBuilder methodBuilder = typeBuilder.DefineMethod( name: methodInfo.Name, attributes: MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual, returnType: methodInfo.ReturnType, parameterTypes: paramTypes); ILGenerator il = methodBuilder.GetILGenerator(); //开始方法调用 il.BeginScope(); //实现接口的肯定是实例类,先加载this il.Emit(OpCodes.Ldarg_0); //字典对象和key压入栈 il.Emit(OpCodes.Ldfld, fieldBuilder); il.Emit(OpCodes.Ldstr, methodInfo.Name); //获取的匿名方法压入栈 il.Emit(OpCodes.Callvirt, typeof(Dictionary<string, Delegate>).GetMethod("get_Item")); //对应的实现 Delegate @delegate = methods[methodInfo.Name]; //调用委托的Invoke方法,需要在栈上加载委托对象和参数集 //因为委托实例中包含目标对象所以不用管 //压入所有形参 for (int j = 0; j < paramTypes.Length; j++) { il.Emit(OpCodes.Ldarg_S, j + 1); } //调用委托 il.Emit(OpCodes.Callvirt, @delegate.GetType().GetMethod("Invoke")); il.Emit(OpCodes.Ret); il.EndScope(); } Type rtnType = typeBuilder.CreateTypeInfo().AsType(); object obj = Activator.CreateInstance(rtnType); //设置实例的方法表 obj.GetType().GetField("_methodTable", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(obj, methods); return (T)obj; } public static Type[] ParamInfoToTypes(MethodInfo methodInfo) { ParameterInfo[] paramInfos = methodInfo.GetParameters(); Type[] paramTypes = new Type[paramInfos.Length]; for (int i = 0; i < paramInfos.Length; i++) { paramTypes[i] = paramInfos[i].ParameterType; } return paramTypes; } } }
文章评论