建立一个方法的attribute,可以放在任意方法上,可以自动记录方法出错时的信息,就不用写try 。。cacth. 【注意】 不是在asp.net MVC下,是在普通三层结构下写的的特性。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; using System.Reflection.Emit; namespace ConsoleApp { class Program { static void Main(string[] args) { TestA test = DynamicProxy.Create<TestA>(); int result = test.TestMethod(1, 2); Console.WriteLine(result); Console.WriteLine("done..."); Console.ReadLine(); } } public class TestA { [Log] public virtual int TestMethod(int a, int b) { return a + b; } } public class AspectContext { public object[] ParameterArgs { get; set; } } public abstract class AspectAttribute : Attribute { public abstract void BeforeInvoke(AspectContext cxt); public abstract void AfterInvoke(AspectContext cxt); } public class LogAttribute : AspectAttribute { public override void BeforeInvoke(AspectContext cxt) { if (cxt != null && cxt.ParameterArgs != null) { foreach (object item in cxt.ParameterArgs) Console.WriteLine(item); } Console.WriteLine("a"); } public override void AfterInvoke(AspectContext cxt) { Console.WriteLine("b"); } } public class DynamicProxy { private static Dictionary<string, object> func_dic = new Dictionary<string, object>(); public static T Create<T>() { return Create<T>(typeof(T)); } public static T Create<T>(Type srcType) { object obj = null; if (!func_dic.TryGetValue(srcType.FullName, out obj)) { lock (func_dic) { if (!func_dic.TryGetValue(srcType.FullName, out obj)) { Type type = CreateProxyType(srcType); obj = CreateFunc<T>(type); func_dic.Add(srcType.FullName, obj); } } } //通过代理创建实例 Func<T> func = obj as Func<T>; if (func == null) throw new Exception("unknown exception"); return func(); } //创建类对象的代理 private static Func<T> CreateFunc<T>(Type type) { DynamicMethod method = new DynamicMethod("", typeof(T), null); var il = method.GetILGenerator(); ConstructorInfo info = type.GetConstructor(Type.EmptyTypes); if (info == null) return null; il.Emit(OpCodes.Newobj, info); il.Emit(OpCodes.Ret); return method.CreateDelegate(typeof(Func<T>)) as Func<T>; } private static Type CreateProxyType(Type srcType) { AssemblyName assemblyName = new AssemblyName(srcType.Name + "_Aop_Assmely"); AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave); ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(srcType.Name + "_Aop_Module"); string typeName = srcType.Name + "_Aop"; TypeAttributes attr = TypeAttributes.Class | TypeAttributes.Public; TypeBuilder typeBuilder = moduleBuilder.DefineType(typeName, attr, srcType, Type.EmptyTypes); MethodInfo[] methods = srcType.GetMethods(BindingFlags.Public | BindingFlags.Instance); foreach (MethodInfo method in methods) OverrideMethods(typeBuilder, method); return typeBuilder.CreateType(); } private static void OverrideMethods(TypeBuilder tb, MethodInfo method) { if (!method.IsPublic|| !method.IsVirtual || IsObjectMethod(method)) return; Type[] paramTypes = GetParameterTypes(method); MethodAttributes attr = MethodAttributes.Public | MethodAttributes.Family | MethodAttributes.HideBySig | MethodAttributes.Virtual; MethodBuilder mb = tb.DefineMethod(method.Name, attr, method.ReturnType, paramTypes); LocalBuilder result = null; ILGenerator il = mb.GetILGenerator(); bool is_void = method.ReturnType != typeof(void); if (is_void == false) result = il.DeclareLocal(method.ReturnType); object[] attrs = method.GetCustomAttributes(typeof(AspectAttribute), false); if (attrs != null) { //初始化所有当前方法用到的参数object[] CreateLocalParameterArr(il, paramTypes); //初始化AspectContext Type ctxType = typeof(AspectContext); ConstructorInfo info = ctxType.GetConstructor(Type.EmptyTypes); var ctx = il.DeclareLocal(ctxType); il.Emit(OpCodes.Newobj, info); il.Emit(OpCodes.Stloc, ctx); //给AspectContext的参数值属性ParameterArgs赋值 var propMethod = ctxType.GetMethod("set_ParameterArgs"); il.Emit(OpCodes.Ldloc, ctx); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Call, propMethod); int m = attrs.Length; LocalBuilder[] lbs = new LocalBuilder[m]; MethodInfo[] endInvokeMethods = new MethodInfo[m]; //初始化标记的横切对象,并调用横切对象的BeforeInvoke方法 for (int i = 0; i < m; i++) { var tmpType = attrs[i].GetType(); var aspect = il.DeclareLocal(tmpType); ConstructorInfo tmpInfo = tmpType.GetConstructor(Type.EmptyTypes); il.Emit(OpCodes.Newobj, tmpInfo); il.Emit(OpCodes.Stloc, aspect); var before_invoke_method = tmpType.GetMethod("BeforeInvoke"); endInvokeMethods[i] = tmpType.GetMethod("AfterInvoke"); il.Emit(OpCodes.Ldloc, aspect); il.Emit(OpCodes.Ldloc, ctx); il.Emit(OpCodes.Callvirt, before_invoke_method); il.Emit(OpCodes.Nop); lbs[i] = aspect; } //类对象,参数值依次入栈 for (int i = 0; i <= paramTypes.Length; i++) il.Emit(OpCodes.Ldarg, i); //调用基类的方法 il.Emit(OpCodes.Call, method); //如果有返回值,保存返回值到局部变量 if (is_void == false) il.Emit(OpCodes.Stloc, result); //调用横切对象的AfterInvoke方法 for (int i = 0; i < m; i++) { il.Emit(OpCodes.Ldloc, lbs[i]); il.Emit(OpCodes.Ldloc, ctx); il.Emit(OpCodes.Callvirt, endInvokeMethods[i]); il.Emit(OpCodes.Nop); } //如果有返回值,则把返回值压栈 if (is_void == false) il.Emit(OpCodes.Ldloc, result); //返回 il.Emit(OpCodes.Ret); } } private static void CreateLocalParameterArr(ILGenerator il, Type[] paramTypes) { il.DeclareLocal(typeof(object[])); il.Emit(OpCodes.Ldc_I4, paramTypes.Length); il.Emit(OpCodes.Newarr, typeof(object)); il.Emit(OpCodes.Stloc_0); for (int i = 0; i < paramTypes.Length; i++) { il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldc_I4, i); il.Emit(OpCodes.Ldarg, i + 1); if (paramTypes[i].IsValueType) il.Emit(OpCodes.Box, paramTypes[i]); il.Emit(OpCodes.Stelem_Ref); } } private static Type[] GetParameterTypes(MethodInfo method) { var paramInfos = method.GetParameters(); int len = paramInfos.Length; Type[] paramTypes = new Type[len]; for (int i = 0; i < len; i++) paramTypes[i] = paramInfos[i].ParameterType; return paramTypes; } //判断是否是基类Object的虚方法 private static bool IsObjectMethod(MethodInfo info) { string[] arr = new string[] { "ToString", "GetType", "GetHashCode", "Equals" }; return arr.Contains(info.Name); } } }
例:AttrbuteTest]//这个特性是用来捕捉方法的异常,并记录到日志
public void Test()
{
string str = "test";
int i = int.Parse(str);
}