HTML5技术

.Net中的AOP系列之《拦截位置》 - tkb至简(3)

字号+ 作者:H5之家 来源:博客园 2016-08-30 18:00 我要评论( )

字段是类级别的变量,这就意味着我们不能找到一种方法显式指定应该如何懒加载一个字段。假设我们以隐式的方式懒加载指定的字段,首先,编写代码如下,这次用的不是属性,而是字段: #region 2.0 懒加载字段private

字段是类级别的变量,这就意味着我们不能找到一种方法显式指定应该如何懒加载一个字段。假设我们以隐式的方式懒加载指定的字段,首先,编写代码如下,这次用的不是属性,而是字段:

#region 2.0 懒加载字段 private static SlowConstructor SlowService; #endregion static void Main(string[] args) { SlowService.DoSomething(); SlowService.DoSomething(); Console.Read(); }

最简单的做法就是使用反射的Activator创建字段类型的实例,下面我们创建一个继承了LocationInterceptionAspect的切面,然后用于该字段:

#region 2.0 懒加载字段 [MyLazyLoadingFieldAspect] private static SlowConstructor SlowService; #endregion static void Main(string[] args) { SlowService.DoSomething(); SlowService.DoSomething(); Console.Read(); } [Serializable] public sealed class MyLazyLoadingFieldAspect : LocationInterceptionAspect { private object _backingField; readonly object _syncRoot=new object(); public override void OnGetValue(LocationInterceptionArgs args) { if (_backingField==null) { lock (_syncRoot) { if (_backingField==null) { _backingField = Activator.CreateInstance(args.Location.LocationType);//Activator会使用位置的类型创建一个新对象 } } } args.Value = _backingField; } } 反射之Activator

反射是位于System.Reflection命名空间下的一系列工具,它允许我们编写一些在程序运行时进行读取或者生成代码的代码。Activator可以创建运行时中对象的新实例,这在直到运行时才知道该实例化哪种类型的对象时很有用。上面的切面可以在任何类型的字段上重复使用,但是这种灵活性也带来了性能损耗,因此,确保必要的时候才使用反射。

上面的代码和之前懒加载属性切面的代码很相似,但是我们这里应该注意的是不同点,比如,这里没有使用args.ProceedGetValue(),而是使用了Activator.CreateInstance()。PostSharp的args.Location.LocationType可以告诉我们被拦截位置的类型Type(字段和属性都可以),有了这个信息,我们就可以使用System.Activator创建那个类型的实例了。和之前一样,将结果存到支持字段_backingField中。

这种方法适用面很窄,更加现实的方式是使用工厂,服务定位器或者IoC容器取代Activator。比如,如果使用的是StructureMap(一个流行的.Net IoC工具),那么可以使用ObjectFactory.GetInstance代替Activator,这种方法可以让我们对更复杂的依赖(即,没有无参构造函数的类)使用懒加载。

使用IoC工具

假设SlowConstructor只有一个构造函数,并且该构造函数有一个IMyService参数,修改之后的代码如下:

public class SlowConstructor { //public SlowConstructor() //{ // Console.WriteLine("正在初始化SlowConstructor,请稍等..."); // Thread.Sleep(5000); //} private IMyService _myService; public SlowConstructor(IMyService myService)//只有一个构造函数,并且需要一个参数 { _myService = myService; Console.WriteLine("正在初始化SlowConstructor,请稍等..."); Thread.Sleep(5000); } //public void DoSomething() //{ // Console.WriteLine("{0}:正在处理一些业务...",DateTime.Now); //} public void DoSomething() { _myService.DoSomething(); } } public interface IMyService { void DoSomething(); } public class MyService:IMyService { public void DoSomething() { Console.WriteLine("{0}:正在处理一些业务...", DateTime.Now); } }

在切面中,仍然可以使用Activator创建对象,但是同时必须创建该对象依赖的对象,在上面的代码中就是MySevice,在一个真实应用中,依赖链可能会更长或更复杂,因此,一般都会把这个任务交给一个工具,比如StructureMap。下面的代码是如何在控制台的Main方法中初始化StructureMap,其它的IoC工具都是类似的【下一个系列教程就是关于DI/IoC的】:

#region 2.0 懒加载字段 //[MyLazyLoadingFieldAspect] [LazyLoadStructureMapAspect] private static SlowConstructor SlowService; #endregion static void Main(string[] args) { //ObjectFactory.Initialize告诉StructureMap使用哪个实现 ObjectFactory.Initialize(cfg => { cfg.For<IMyService>().Use<MyService>();//当调用IMyService的构造函数时,使用MyService作为实现 cfg.For<SlowConstructor>().Use<SlowConstructor>();//这行代码可选,StructureMap会自动绑定 }); SlowService.DoSomething(); SlowService.DoSomething(); Console.Read(); }

现在依赖配置好了,并且给字段添加了新的特性切面。这里简单介绍一下StructureMap的依赖配置,下一个系列教程会详细讲解哦!首先使用ObjectFactory.Initialize【已经过时了,在新版本已经不建议使用这种方式】指定依赖,如果StructureMap请求IMyService的实现,那么就会返回MyService对象,如果请求的是SlowConstructor,那么就会使用SlowConstructor。更重要的是,当创建SlowConstructor的实例时,它会识别出SlowConstructor的构造函数有一个IMyService类型的参数,因此会自动使用配置的依赖并传给该构造函数MyService的新实例。

下面我们需要创建一个新切面,和之前使用Activator的例子看起来很像,但是这次使用了ObjectFactory.GetInstance而不是Activator,这样StructureMap会自动提供需要的对象:

[Serializable] public class LazyLoadStructureMapAspect:LocationInterceptionAspect { private object _backingField; readonly object _syncRoot=new object(); public override void OnGetValue(LocationInterceptionArgs args) { if (_backingField==null) { lock (_syncRoot) { if (_backingField==null) { var locationType = args.Location.PropertyInfo.PropertyType; _backingField= ObjectFactory.GetInstance(locationType); } } } args.Value = _backingField; } }

执行结果和之前的一样,只不过这次的例子更加具有现实意义,因为类有关于接口的依赖,配置这些依赖使用了IoC工具。

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • 升讯威微信营销系统开发实践:(3)中控服务器的设计 .Net 还是 Java? - sheng.chao

    升讯威微信营销系统开发实践:(3)中控服务器的设计 .Net 还是 Java

    2016-08-26 16:00

  • 开源:ASP.NET MVC+EF6+Bootstrap开发框架 - NFine

    开源:ASP.NET MVC+EF6+Bootstrap开发框架 - NFine

    2016-08-24 11:00

  • ASP.NET 关于GridView 表格重复列合并 - 小飞飞oo

    ASP.NET 关于GridView 表格重复列合并 - 小飞飞oo

    2016-08-23 10:02

  • 拥抱.NET Core,如何开发一个跨平台类库 (1) - KAnts

    拥抱.NET Core,如何开发一个跨平台类库 (1) - KAnts

    2016-08-08 14:00

网友点评
-