AOP在 .NET中的七种实现方法
这里列表了我想到的在应用程序中加入AOP支持的所有方法。这里最主要的焦点是拦截,因为一旦有了拦截其它的事情都是细节。
Approach 方法 |
Advantages 优点 |
Disadvantages 缺点 |
Remoting Proxies 远程代理 |
Easy to implement, because of the .Net framework support 容易实现,因为有.NET框架的支持。 |
Somewhat heavyweight 微显重量级 仅在接口或MarshalByRefObjects 上使用 |
Derivingfrom ContextBoundObject 从ContextBoundObject 派生 |
Easiest to implement 很容易实现 原生支持调用拦截 |
Very costly in terms of performance 非常昂贵的性能代价 |
Compile-time subclassing 编译时子类化 |
Easiest to understand 很容易理解 |
Interfaces or virtual methods only 仅用于接口或虚方法 |
Runtime subclassing 运行时子类化 |
Easiest to understand 很容易理解 非常灵活 |
Complex implementation (but alreadyexists) 复杂的实现(已经实现) 仅用于接口或虚方法 |
Hooking into the profiler API 分析 API钩子 |
Extremely powerful 极端强大 |
Performance? 性能未知 复杂实现(COM API,需要单独运行等) |
Compile time IL-weaving 编译时 IL织入 |
Very powerful 非常强大 良好的性能 |
Very hard to implement 实现非常困难 |
Runtime IL-weaving 运行时 IL织入 |
Very powerful 非常强大 朗好的性能 |
Very hard to implement 实现非常困难 |
英文原文 7 Approaches for AOP in .Net
1.远程代理
使用.Net Remoting/RealProxy
采用TransparentProxy和RealProxy实现对象的代理,实现思路如下:Client -TransparentProxy - RealProxy - Target Object
下面实现自定义的TransparentProxy和RealProxy
![](/assets/blank.gif)
![](/assets/blank.gif)
using System; using System.Runtime.Remoting.Proxies; using System.Runtime.Remoting.Messaging;namespace ConsoleApplication1 {public class User{public string Name;public string PassWord;}//用户注册接口和实现public interface IUserProcessor{void RegUser(User user);}public class UserProcessor : MarshalByRefObject, IUserProcessor{public void RegUser(User user){Console.WriteLine("用户已注册。");}}//客户端调用class Program{ static void Main(string[] args){try{User user = new User() { Name = "lee", PassWord = "123456" };UserProcessor userprocessor = TransparentProxy.Create<UserProcessor>();userprocessor.RegUser(user);}catch (Exception ex){throw ex;} Console.ReadKey();} }//RealProxy public class MyRealProxy<T> : RealProxy{private T _target;public MyRealProxy(T target): base(typeof(T)){this._target = target;}public override IMessage Invoke(IMessage msg){PreProceede(msg);IMethodCallMessage callMessage = (IMethodCallMessage)msg;object returnValue = callMessage.MethodBase.Invoke(this._target, callMessage.Args);PostProceede(msg);return new ReturnMessage(returnValue, new object[0], 0, null, callMessage);}public void PreProceede(IMessage msg){Console.WriteLine("方法执行前");}public void PostProceede(IMessage msg){Console.WriteLine("方法执行后");}}//TransparentProxy public static class TransparentProxy{public static T Create<T>(){T instance = Activator.CreateInstance<T>();MyRealProxy<T> realProxy = new MyRealProxy<T>(instance);T transparentProxy = (T)realProxy.GetTransparentProxy();return transparentProxy;}} }
View Code
优点:简单实现
缺点:性能差
2. 继承ContextBoundObject方式
![](/assets/blank.gif)
![](/assets/blank.gif)
using System; using System.Runtime.Remoting.Proxies; using System.Runtime.Remoting.Messaging; using System.Runtime.Remoting.Activation;namespace ConsoleApplication4 {class Program{static void Main(string[] args){Console.WriteLine();AopClass ap = new AopClass("XiaoQiang");Console.WriteLine("Show:" + ap.Hello());Console.WriteLine(ap.ToString());Console.WriteLine();Console.WriteLine("Show:" + ap.Say("hello,everybody!"));Console.WriteLine(ap.ToString());Console.WriteLine(ap.ToString());Console.WriteLine();WatchToDo(ToDoA);WatchToDo(ToDoB);Console.ReadKey();}#region 性能测试static void ToDoA(){ClsA cl1 = new ClsA();cl1.ToDo();}static void ToDoB(){ClsB cl2 = new ClsB();cl2.ToDo();}static long WatchToDo(Action act){System.Diagnostics.Stopwatch stop = new System.Diagnostics.Stopwatch();stop.Reset();stop.Start();for (int i = 0; i < 10000; i++)act();stop.Stop();Console.WriteLine("Time: " + stop.ElapsedTicks);return stop.ElapsedTicks;}#endregion}#region AopAttribute[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]public class AopAttribute : ProxyAttribute{private IAopProxyBuilder builder = null;public AopAttribute(Type builderType){this.builder = (IAopProxyBuilder)Activator.CreateInstance(builderType);}public override MarshalByRefObject CreateInstance(Type serverType){AopProxy realProxy = new AopProxy(serverType);return realProxy.GetTransparentProxy() as MarshalByRefObject;}}#endregion#region MethodAopAdviceAttribute[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]public class MethodAopAdviceAttribute : Attribute{private AdviceType type = AdviceType.None;public MethodAopAdviceAttribute(AdviceType advicetype){this.type = advicetype;}public AdviceType AdviceType{get{return this.type;}}}public enum AdviceType{None,Before,After,Around}#endregion#region AopProxypartial interface IAopAction{void PreProcess(IMessage requestMsg);void PostProcess(IMessage requestMsg, IMessage Respond);}public class AopProxy : RealProxy, IAopAction{public AopProxy(Type serverType): base(serverType){ }public virtual void PreProcess(object obj, string method = "", int argCount = 0, object[] args = null){var o = obj as AopClass;if (o != null){o.IsLock = false;o.Name = "999999";}}public virtual void PreProcess(IMessage requestMsg){var o = GetUnwrappedServer();IMethodCallMessage call = requestMsg as IMethodCallMessage;if (call != null){this.PreProcess(o, call.MethodName, call.InArgCount, call.Args);}else{this.PreProcess(o);}}public virtual void PostProcess(object obj, object returnValue = null, string method = "", int argCount = 0, object[] args = null){var o = obj as AopClass;if (o != null){o.IsLock = true;o.Name = "10101010";}}public virtual void PostProcess(IMessage requestMsg, IMessage Respond){var o = GetUnwrappedServer();ReturnMessage mm = Respond as ReturnMessage;var ret = mm.ReturnValue;IMethodCallMessage call = requestMsg as IMethodCallMessage;if (call != null){this.PostProcess(o, ret, call.MethodName, call.InArgCount, call.Args);}else{this.PostProcess(o, ret);}}//public virtual IMessage Proessed(IMessage msg,MarshalByRefObject target)//{// IMethodCallMessage call = (IMethodCallMessage)msg;// IConstructionCallMessage ctor = call as IConstructionCallMessage;// if (ctor != null)// {// //获取最底层的默认真实代理 // RealProxy default_proxy = System.Runtime.Remoting.RemotingServices.GetRealProxy(target);// default_proxy.InitializeServerObject(ctor);// MarshalByRefObject tp = (MarshalByRefObject)this.GetTransparentProxy();// //自定义的透明代理 this // return System.Runtime.Remoting.Services.EnterpriseServicesHelper.CreateConstructionReturnMessage(ctor, tp);// }// IMethodReturnMessage result_msg = System.Runtime.Remoting.RemotingServices.ExecuteMessage(target, call);// //将消息转化为堆栈,并执行目标方法,方法完成后,再将堆栈转化为消息 // return result_msg;//}public virtual IMessage Proessed(IMessage msg){IMessage message;if (msg is IConstructionCallMessage){message = this.ProcessConstruct(msg);}else{message = this.ProcessInvoke(msg);}return message;}public virtual void ChangeReturnValue(IMessage msg, ref object o){if (msg is IMethodCallMessage){var m = msg as IMethodCallMessage;string name = m.MethodName;if (name == "Hello")o = "Hello,Lucy!";}}public virtual IMessage ProcessInvoke(IMessage msg){IMethodCallMessage callMsg = msg as IMethodCallMessage;IMessage message;try{object[] args = callMsg.Args; //方法参数 object o = callMsg.MethodBase.Invoke(GetUnwrappedServer(), args); //调用 原型类的 方法 ChangeReturnValue(msg, ref o);message = new ReturnMessage(o, args, args.Length, callMsg.LogicalCallContext, callMsg); // 返回类型 Message }catch (Exception e){message = new ReturnMessage(e, callMsg);}//Console.WriteLine("Call Method:"+callMsg.MethodName);//Console.WriteLine("Return:"+ message.Properties["__Return"].ToString());return message;}public virtual IMessage ProcessConstruct(IMessage msg){IConstructionCallMessage constructCallMsg = msg as IConstructionCallMessage;//构造函数 初始化IConstructionReturnMessage constructionReturnMessage = this.InitializeServerObject((IConstructionCallMessage)msg);RealProxy.SetStubData(this, constructionReturnMessage.ReturnValue);//Console.WriteLine("Call constructor:"+constructCallMsg.MethodName);//Console.WriteLine("Call constructor arg count:"+constructCallMsg.ArgCount);return constructionReturnMessage;}public override IMessage Invoke(IMessage msg){#region 获取AdviceTypeAdviceType type = AdviceType.None;IMethodCallMessage call = (IMethodCallMessage)msg;foreach (Attribute attr in call.MethodBase.GetCustomAttributes(false)){MethodAopAdviceAttribute mehodAopAttr = attr as MethodAopAdviceAttribute;if (mehodAopAttr != null){type = mehodAopAttr.AdviceType;break;}}#endregionIMessage message;if (type == AdviceType.Before || type == AdviceType.Around){this.PreProcess(msg);Console.WriteLine("::Before Or Around");}message = this.Proessed(msg);if (type == AdviceType.After || type == AdviceType.Around){this.PostProcess(msg, message);Console.WriteLine("::After Or Around");}return message;}}#endregion#region AopProxyBuilderpublic interface IAopProxyBuilder{AopProxy CreateAopProxyInstance(Type type);}public class AopProxyBuilder : IAopProxyBuilder{public AopProxy CreateAopProxyInstance(Type type){return new AopProxy(type);}}#endregion#region AopClass[AopAttribute(typeof(AopProxyBuilder))]public class AopClass : ContextBoundObject{public string Name{ get; set; }public bool IsLock = true;public AopClass(string name){Name = name;Console.WriteLine("Aop Class Create Name:" + Name);}public AopClass(){Console.WriteLine("Aop Class Create");}[MethodAopAdvice(AdviceType.Around)]public string Hello(){Console.WriteLine("hello world:");return "hello world:";}[MethodAopAdvice(AdviceType.Before)]public string Say(string content){string c = "IsLock:" + IsLock + "\t " + Name + " :" + content;Console.WriteLine(c);return c;}public override string ToString(){return string.Format("Name:{0},IsLock:{1}", this.Name, this.IsLock);}}#endregion#region 测试数据public class ClsA{public void ToDo(){}}public class ClsB : ContextBoundObject{public void ToDo(){}}#endregion }
View Code
测试结果:性能时间后者是前者的318倍
3.EnterpriseLibary 实现方法
![](/assets/blank.gif)
![](/assets/blank.gif)
//首先添加EnterpriseLibary的引用//自定义CallHandler,这里定义两个CallHandler分别用于参数检查和日志记录。using Microsoft.Practices.Unity.InterceptionExtension;public class UserHandler : ICallHandler {public int Order { get; set; }public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext){User user = input.Inputs[0] as User;if (user.PassWord.Length < 10){return input.CreateExceptionMethodReturn(new UserException("密码长度不能小于10位"));}Console.WriteLine("参数检测无误");return getNext()(input, getNext);} }public class LogHandler : ICallHandler {public int Order { get; set; }public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext){User user = input.Inputs[0] as User;Log log = new Log() { Message = string.Format("RegUser:Username:{0},Password:{1}", user.Name, user.PassWord), Ctime = DateTime.Now };Console.WriteLine("日志已记录,Message:{0},Ctime:{1}", log.Message, log.Ctime);var messagereturn = getNext()(input, getNext);return messagereturn;} }//定义对应的HandlerAttribute using Microsoft.Practices.Unity.InterceptionExtension; using Microsoft.Practices.Unity; public class UserHandlerAttribute : HandlerAttribute { public override ICallHandler CreateHandler(IUnityContainer container) { ICallHandler handler = new UserHandler(){Order=this.Order}; return handler; } } public class LogHandlerAttribute:HandlerAttribute { public int Order { get; set; } public override ICallHandler CreateHandler(IUnityContainer container) { return new LogHandler() { Order = this.Order }; } } //用户注册接口和实现,这里通过为接口添加attribute的方式实现。order值表示执行顺序,值小的先执行。[LogHandlerAttribute(Order=2)] [UserHandlerAttribute(Order=1)] public interface IUserProcessor { void RegUser(User user); } public class UserProcessor : MarshalByRefObject,IUserProcessor { public void RegUser(User user) { Console.WriteLine("用户已注册。"); } } //测试using Microsoft.Practices.EnterpriseLibrary.PolicyInjection; public class Client { public static void Run() { try { User user = new User() { Name = "lee", PassWord = "123123123123" }; UserProcessor userprocessor = PolicyInjection.Create<UserProcessor>(); userprocessor.RegUser(user); } catch(Exception ex) { throw ex; } } }
View Code
4.Castle Dynamic Proxy的实现
首先下载Castle.Windsor.dll
自定义Interceptor
public class MyInterceptor : IInterceptor{public void Intercept(IInvocation invocation){PreProceed(invocation);invocation.Proceed();PostProceed(invocation);}public void PreProceed(IInvocation invocation){Console.WriteLine("方法执行前");}public void PostProceed(IInvocation invocation){Console.WriteLine("方法执行后");}}
用户注册接口和实现
public interface IUserProcessor{void RegUser(User user);}public class UserProcessor : IUserProcessor{public virtual void RegUser(User user){Console.WriteLine("用户已注册。Name:{0},PassWord:{1}", user.Name, user.PassWord);}}
客户端调用
public class Client{public static void Run(){try{ProxyGenerator generator = new ProxyGenerator();MyInterceptor interceptor = new MyInterceptor();UserProcessor userprocessor = generator.CreateClassProxy<UserProcessor>(interceptor);User user= new User() { Name = "lee", PassWord = "123123123123" };userprocessor.RegUser(user);}catch (Exception ex){throw ex;}}}
6.IL织入 PostSharp实现AOP
原文链接
PostSharp简介
PostSharp是一个用于在.NET平台上实现AOP的框架,是我比较常用的一个AOP框架,官方网站为http://www.sharpcrafters.com。目前最新版本为2.0,但是2.0的license不再免费,因此个人建议下载1.5版,同时下文都是基于PostSharp1.5。
PostSharp使用静态织入方式实现AOP,其连接点非常丰富,使用简单,而且相对其它一些.NET平台上的AOP框架来说,PostSharp较为轻量级,但是功能却一点也不逊色,因此是我比较喜欢的一个AOP框架。更多关于PostSharp的介绍请参看其官方网站。
另外使用PostSharp与其它框架不太一样的是一定要下载安装包安装,只引用类库是不行的,因为上文说过,AOP框架需要为编译器或运行时添加扩展。
使用PostSharp实现AOP示例
这一节将通过一个例子演示如何使用PostSharp在.NET平台上实现AOP。这个例子将通过AOP为核心业务函数增加日志记录功能。
新建项目
首先新建一个C#的WinForm应用程序,如图4所示,这里将工程命名为“PostSharpExample”。
图4、新建项目
编写核心业务函数
首先我们来编写核心业务。当然这里不存在真正的业务,我们只是模拟一个而已。将要模拟的核心业务是预定房间。先构建一个如图5所示的简单UI。
图5、UI界面
下面我们为项目增加一个“CoreBusiness”类,并在其中添加“Subscribe”方法。代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
|
using System;
namespace PostSharpExample
{
public class CoreBusiness
{
public static void Describe(string memberName, string roomNumber)
{
System.Windows.Forms.MessageBox.Show(String.Format( "尊敬的会员{0},恭喜您预定房间{1}成功!" , memberName, roomNumber), "提示" );
}
}
}
|
可以看到,这里Subscribe方法仅仅是输出一个提示框。当然,在真正项目中这种输出型代码不应该写在业务逻辑中,这里这样写主要是为了演示方便。然后,我们在Form1中调用Subscribe业务方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
using System;
using System.Windows.Forms;
namespace PostSharpExample
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void BTN_SUBSCRIBE_Click(object sender, EventArgs e)
{
if (!String.IsNullOrEmpty(TXB_NAME.Text.Trim()) && !String.IsNullOrEmpty(TXB_ROOM.Text.Trim()))
CoreBusiness.Describe(TXB_NAME.Text.Trim(), TXB_ROOM.Text.Trim());
else
MessageBox.Show( "信息不完整" , "提示" );
}
}
}
|
运行程序就可以看到相应的效果:
图6、预定房间成功演示效果
使用AOP增加日志记录功能
现在加入我们要为程序添加日志功能,记录业务函数的执行情况。这里我们假定需要将日志记录到纯文本文件中,首先我们完成日志记录工具类,LoggingHelper。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
using System;
using System.IO;
namespace PostSharpExample
{
class LoggingHelper
{
private const String _errLogFilePath = @ "log.txt" ;
public static void Writelog(String message)
{
StreamWriter sw = new StreamWriter(_errLogFilePath, true );
String logContent = String.Format( "[{0}]{1}" , DateTime.Now.ToString( "yyyy-MM-dd hh:mm:ss" ), message);
sw.WriteLine(logContent);
sw.Flush();
sw.Close();
}
}
}
|
如果不使用AOP,则我们要为包括Subscribe在内的每一个方法在核心业务代码的前后插入日志记录代码(Writelog),我们看看使用PostSharp如何将这种横切关注点分离出来。因为要使用PostSharp,所以要先添加对PostSharp库文件的引用,安装过PostSharp后,在系统可引用项中会多出“PostSharp.Laos”、“PostSharp.Public”和“PostSharp.AspNet”,这里我们做的是Winform程序,所以只需添加对“PostSharp.Laos”和“PostSharp.Public”的引用即可。
下面我们就要写Aspect了,PostSharp的Aspect是使用Attribute实现的,下面是我实现的日志记录Aspect代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
using System;
using PostSharp.Laos;
namespace PostSharpExample
{
[Serializable]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true , Inherited = true )]
public sealed class LoggingAttribute : OnMethodBoundaryAspect
{
public string BusinessName { get; set; }
public override void OnEntry(MethodExecutionEventArgs eventArgs)
{
LoggingHelper.Writelog(BusinessName + "开始执行" );
}
public override void OnExit(MethodExecutionEventArgs eventArgs)
{
LoggingHelper.Writelog(BusinessName + "成功完成" );
}
}
}
|
我们约定每个Aspect类的命名必须为“XXXAttribute”的形式。其中“XXX”就是这个Aspect的名字。PostSharp中提供了丰富的内置“Base Aspect”以便我们继承,其中这里我们继承“OnMethodBoundaryAspect ”,这个Aspect提供了进入、退出函数等连接点方法。另外,Aspect上必须设置“[Serializable] ”,这与PostSharp内部对Aspect的生命周期管理有关,具体为什么请参看这里。
我们的LoggingAttribute非常简单,就是在进入(Entry)和离开(Exit)函数时分别记录日志到log文件。现在我们把这个Aspect应用到业务方法上:
1
2
3
4
5
|
[Logging(BusinessName= "预定房间" )]
public static void Describe(string memberName, string roomNumber)
{
System.Windows.Forms.MessageBox.Show(String.Format( "尊敬的会员{0},恭喜您预定房间{1}成功!" , memberName, roomNumber), "提示" );
}
|
可以看到,应用Aspect非常简单,就是将相应的Attribute加到业务方法上面。现在我们再运行预定房间程序,结果和上次没什么两样,但是如果我们打开程序目录,会看到多了一个“log.txt”文件,里面记录有类似图7的内容。
图7、日志内容
可以看到,我们已经通过AOP实现了日志记录功能。通过AOP将横切关注点分离出来后,日志记录的代码都放在LoggingAttribute里,需要修改只要修改一处即可。同时,业务方法仅含有业务代码,这样大大提高了程序代码的可读性和可维护性。
对PostSharp运行机制的简要分析
上文已经说到,PostSharp使用的是静态织入技术,下面我们分析一下PostSharp是如何实现的。
首先,当安装PostSharp时,它自动为Visual Studio编译器添加了AOP扩展。如果仔细观察PostSharpExample编译信息,会发现有这么两行:
图8、PostSharp编译信息
很明显,在.NET Complier编译完成后,下面PostSharp又做了一部分工作,这部分工作就是静态织入的过程。如果我们用.NET Reflector查看PostSharpExample.exe中Subscribe方法的反编译代码,会发现多了很多东西:
图9、织入Aspect后的Describe代码(由.NET Reflector反编译)
这些多出来的代码,就是PostSharp静态织入进去的。当然,这些代码在每次编译完成后,PostSharp都会重新织入一次,所以整个过程对程序员是透明的,我们只需维护纯净的业务代码和Aspect代码即可。
使用PostSharp的优点和缺点(即使用AOP的优点和缺点)
总体来说,使用PostSharp,将会带来如下优点:
- 横切关注点单独分离出来,提高了代码的清晰性和可维护性。
- 只要在Aspect中编写辅助性功能代码,在一定程度上减少了工作量和冗余代码。
当然,使用PostSharp也不是没有缺点,主要缺点有如下两方面:
- 增加了调试的难度。
- 相比于不用AOP的代码,运行效率有所降低。
所以,对于是否引入AOP,请根据项目具体情况,权衡而定。
对于PostSharp的进一步学习
本文只是简要介绍了PostSharp以及实现了一个小例子,并不打算详细完整地介绍PostSharp的方方面面,而只想起到一个抛砖引玉的作用。PostSharp还有非常丰富的功能等待各位学习,因此,如果您对PostSharp十分有兴趣,想进一步学习,请参看PostSharp官方参考文档。
相关下载
本文用到的Example请点击这里下载。
PostSharp1.5安装包请点击这里下载。
5. 各种IoC框架下实现AOP
首先介绍几种笔者常见的IOC框架
Castle
AOP框架
Encase 是C#编写开发的为.NET平台提供的AOP框架。Encase 独特的提供了把方面(aspects)部署到运行时代码,而其它AOP框架依赖配置文件的方式。这种部署方面(aspects)的方法帮助缺少经验的开发人员提高开发效率。
NKalore是一款编程语言,它扩展了C#允许在.net平台使用AOP。NKalore的语法简单、直观,它的编译器是基于Mono C#编译器(MCS)。NKalore目前只能在命令行或#Develop内部使用。NKalore兼容公共语言规范CLS(Common Language Specification),它可以在任何.NET开发环境中使用,包括微软的Visual Studio .NET。
PostSharp读取.NET字节模块,转换成对象模型。让插件分析和转换这个模型并写回到MSIL。PostSharp使开发程序分析应用程序容易得像分析代码规则和设计模式,它使程序开发的思想变革为面向方面软件开发(AOSD/AOD)思想。
AspectDNG的目标是为.NET开发人员提供简单而功能强大的AOP-GAOP实现。它效仿java下的开源工具AspectJ 和 Spoon,成熟程度也很接近它们。
RAIL(Runtime Assembly Instrumentation Library) 开源项目可以在C#程序集加载和运行前进行处理控制调整和重新构建。C#在CLR中,我们已经能够动态加载程序集并且获得程序集中的类和方法,RAIL(Runtime Assembly Instrumentation Library)的出现填补了CLR处理过程中的一些空白。
SetPoint是一款.NET框架下的全功能(full-featured)AOP引擎.它着重为称为语义切点(semantic pointcuts)的定义依赖RDF/OWL的使用.它的功能为一个IL-level,highly dynamic weaver&LENDL,一个引人注目的定义语言、、、、、、
DotNetAOP为 CLR language提供AOP 框架基础属性。
NAop是一个DotNet下的AOP框架。
AspectSharp是DotNet下的免费AOP框架,它以Dynamic Proxies和XML作为配置文件。
参考文章
1.Net平台AOP技术研究
2C#进阶系列——AOP?AOP!
3.NET中的AOP(厉害啊)
转载于:https://www.cnblogs.com/code1992/p/9470396.html
AOP在 .NET中的七种实现方法相关推荐
- 10截图时屏幕变大_解锁电脑更多的截屏姿势:Win 10中自带的七种截图方法
说起电脑截图,我们很容易想到的是微信中Alt+A的快捷方式,但如果我们电脑中没有安装微信却要对屏幕内容进行截图应该怎么办?马上下载并安装微信?没有那么复杂,Win 10就自带了多达七种截图方法,熟悉其 ...
- LINUX中的七种文件类型
在liunx下一切皆文件. LINUX中的七种文件类型 d 目录文件. l 符号链接(指向另一个文件,类似于window下的快捷方式): s 套接字文件: b 块设备文件,二进制文件: c 字符设备文 ...
- 管理工作中的“七种浪费”【转】
丰田生产方式中所归纳的"七种浪费",主要发生在生产现场,但是产生这些浪费的深层次的原因是什么?如果仅仅关注现场存在的浪费,而不解决被现象所掩盖的本质问题,无疑是舍本逐末.为了使消除 ...
- 管理工作中的“七种浪费”
丰田生产方式中所归纳的"七种"浪费,主要发生在生产现场,但是产生这些浪费的深层次的原因是什么?如果仅仅关注现场存在的问题,而不解决被现象所掩盖的本质问题,无疑是舍本逐末,即使表面上 ...
- C库函数qsort七种使用方法示例
这篇文章是 一楼那个学长写的 C库函数qsort七种使用方法示例 七种qsort排序方法<本文中排序都是采用的从小到大排序> 一.对int类型数组排序 C++代码 int num[10 ...
- C语言运行时电脑白屏怎么办,win10电脑白屏死机无响怎么回事_win10电脑白屏死机无响七种解决方法...
win10专业版使用一段时间后出现白屏死机的问题,开机后不是登录界面,而是白色屏幕,尝试多次还是一样,怎么办呢?此故障原因无非是硬件或软件两大原因,有什么办法解决呢?此文小编告诉大家解决win10电脑 ...
- html语言单词背不下来,专八词汇背不下来?来看看这七种记忆方法
专八词汇背不下来?来看看这七种记忆方法 01 结合构词法记忆单词 前缀记忆,英语单词前缀很多. 常用前缀有: anti-(反对,防止),co-(共同),de-(离开,除去),dis-(否定,相反), ...
- Win10管理员命令提示符窗口的七种打开方法汇总
Win10管理员命令提示符窗口的七种打开方法汇总 管理员命令提示符是我们在Windows10系统操作中经常使用的功能之一, 管理员命令提示符可以非常直接的运行各种程序.命令等. 如:在管理员命令提示符 ...
- Oracle的join默认为,Oracle中的三种Join方法详解
这里将为大家介绍Oracle中的三种Join方法,Nested loop join.Sort merge join和Hash join.整理出来以便帮助大家学习. 基本概念 Nested loop j ...
最新文章
- 参加UI培训就业多长时间
- Android中pendingIntent的深入理解
- 中国滤清器制造行业竞争格局分析与发展战略规划研究报告2022年版
- 运动会成绩管理java代码_基于jsp的运动会成绩管理-JavaEE实现运动会成绩管理 - java项目源码...
- java学习笔记:Storm 常用配置
- 漫谈数据库索引 | 脚印 footprint(转载)
- LiveVideoStack秋季招聘(编辑、会务运营、项目经理)
- boot返回码规范 spring_sprigboot-new-coding-standards
- 性能提升一个数量级,大杀器来了!| 文内福利
- c#操作XML文件 1614260503
- 怎样使用python画复杂函数_在python中绘制复杂的函数?
- Java 中的array数组总结之一
- 十年软件开发经验小结
- Windows环境上装在VM,VM安装CentOS7
- linux设置定时关机命令,linux定时关机命令是什么?
- CFS内网渗透三层内网漫游靶场-2019某CTF案例
- epson连接计算机后无法打印,如何解决连接到Epson打印机后计算机无法打印的问题...
- 使用阿里iconfont unicode格式图标
- MySQL 事务的特性
- 刘宇凡:海子,一个孤独的灵魂诗人