C#中增强类功能的几种方式详解
前言
本文主要讲解如何利用C#语言自身的特性来对一个类的功能进行丰富与增强,便于拓展现有项目的一些功能。
拓展方法
扩展方法被定义为静态方法,通过实例方法语法进行调用。方法的第一个参数指定该方法作用于哪个类型,并且该参数以this修饰符为前缀。仅当使用using指令将命名空间显式导入到源代码中之后,扩展方法才可使用。
namespaceExtensions { publicstaticclassStringExtension { publicstaticDateTimeToDateTime(thisstringsource) { DateTime.TryParse(source,outDateTimeresult); returnresult; } } }
注意:
- 如果扩展方法与该类型中定义的方法具有相同的签名,则扩展方法永远不会被调用。
- 在命名空间级别将扩展方法置于相应的作用范围内。例如,在一个名为Extensions的命名空间中具有多个包含扩展方法的静态类,则在使用这些拓展方法时,必须引用其命名空间usingExtensions
继承
继承面向对象的一个特性,属于Isa关系,比如说Student继承Person,则说明StudentisaPerson。子类可以通过重写父类的方法或添加新的方法来实现对父类的拓展。
namespaceInherit { publicclassPersion { publicstringName{get;set;} publicintAge{get;set;} publicvoidEat() { Console.WriteLine("吃饭"); } publicvoidSleep() { Console.WriteLine("睡觉"); } } publicclassStudent:Persion { publicvoidStudy() { Console.WriteLine("学习"); } publicnewvoidSleep() { Console.WriteLine("做作业,复习功课"); base.Sleep(); } } }
继承的缺点:
- 父类的内部细节对子类是可见的
- 子类与父类的继承关系在编译阶段就确定下来了,无法在运行时动态改变从父类继承方法的行为
- 如果父类方法做了修改,所有的子类都必须做出相应的调整,子类与父类是一种高度耦合,违反了面向对象的思想。
组合
组合就是在设计类的时候把需要用到的类作为成员变量加入到当前类中。
组合的优缺点:
优点:
- 隐藏了被引用对象的内部细节
- 降低了两个对象之间的耦合
- 可以在运行时动态修改被引用对象的实例
缺点:
- 系统变更可能需要不停的定义新的类
- 系统结构变复杂,不再局限于单个类
建议多使用组合,少用继承
装饰者模式
装饰者模式指在不改变原类定义及继承关系的情况跟下,动态的拓展一个类的功能,就是利用创建一个包装类(wrapper)来装饰(decorator)一个已有的类。
包含角色:
被装饰者:
- Component抽象被装饰者,
- ConcreteComponent具体被装饰者,Component的实现,在装饰者模式中装饰的就是这货。
装饰者:
- Decorator装饰者一般是一个抽象类并且作为Component的子类,Decorator必然会有一个成员变量用来存储Component的实例
- ConcreateDecorator具体装饰者Decorator的实现
在装饰者模式中必然会有一个最基本,最核心,最原始的接口或抽象类充当component和decorator的抽象组件
实现要点:
- 定义一个类或接口,并且让装饰者及被装饰者的都继承或实现这个类或接口
- 装饰者中必须持有被装饰者的引用
- 装饰者中对需要增强的方法进行增强,不需要增强的方法调用原来的业务逻辑
namespaceDecorator { //////Component抽象者装饰者 /// publicinterfaceIStudent { voidLearn(); } //////ConcreteComponent具体被装饰者 /// publicclassStudent:IStudent { privatestring_name; publicStudent(stringname) { this._name=name; } publicvoidLearn() { System.Console.WriteLine(this._name+"学习了以上内容"); } } //////Decorator装饰者 /// publicabstractclassTeacher:IStudent { privateIStudent_student; publicTeacher(IStudentstudent) { this._student=student; } publicvirtualvoidLearn() { this.Rest(); this._student.Learn(); } publicvirtualvoidRest() { Console.WriteLine("课间休息"); } } //////ConcreteDecorator具体装饰者 /// publicclassMathTeacher:Teacher { privateString_course; publicMathTeacher(IStudentstudent,stringcourse):base(student) { this._course=course; } publicoverridevoidLearn() { System.Console.WriteLine("学习新内容:"+this._course); base.Learn(); } publicoverridevoidRest() { System.Console.WriteLine("课间不休息,开始考试"); } } //////ConcreteDecorator具体装饰者 /// publicclassEnlishTeacher:Teacher { privateString_course; publicEnlishTeacher(IStudentstudent,stringcourse):base(student) { this._course=course; } publicoverridevoidLearn() { this.Review(); System.Console.WriteLine("学习新内容:"+this._course); base.Learn(); } publicvoidReview() { System.Console.WriteLine("复习英文单词"); } } publicclassProgram { staticvoidMain(string[]args) { IStudentstudent=newStudent("student"); student=newMathTeacher(student,"高数"); student=newEnlishTeacher(student,"英语"); student.Learn(); } } }
装饰者模式优缺点:
优点:
- 装饰者与被装饰者可以独立发展,不会互相耦合
- 可以作为继承关系的替代方案,在运行时动态拓展类的功能
- 通过使用不同的装饰者类或不同的装饰者排序,可以得到各种不同的结果
缺点:
- 产生很多装饰者类
- 多层装饰复杂
代理模式
代理模式就是给一个对象提供一个代理对象,并且由代理控制原对象的引用。
包含角色:
- 抽象角色:抽象角色是代理角色和被代理角色的所共同继承或实现的抽象类或接口
- 代理角色:代理角色是持有被代理角色引用的类,代理角色可以在执行被代理角色的操作时附加自己的操作
- 被代理角色:被代理角色是代理角色所代理的对象,是真实要操作的对象
静态代理
动态代理涉及到反射技术相对静态代理会复杂很多,掌握好动态代理对AOP技术有很大帮助
namespaceProxy { //////共同抽象角色 /// publicinterfaceIBuyHouse { voidBuy(); } //////真实买房人,被代理角色 /// publicclassCustomer:IBuyHouse { publicvoidBuy() { System.Console.WriteLine("买房子"); } } //////中介-代理角色 /// publicclassCustomerProxy:IBuyHouse { privateIBuyHousetarget; publicCustomerProxy(IBuyHousebuyHouse) { this.target=buyHouse; } publicvoidBuy() { System.Console.WriteLine("筛选符合条件的房源"); this.target.Buy(); } } publicclassProgram { staticvoidMain(string[]args) { IBuyHousebuyHouse=newCustomerProxy(newCustomer()); buyHouse.Buy(); System.Console.ReadKey(); } } }
动态代理
namespaceDynamicProxy { usingMicrosoft.Extensions.DependencyInjection; usingSystem; usingSystem.Collections.Generic; usingSystem.Linq; usingSystem.Linq.Expressions; usingSystem.Reflection; //////方法拦截器接口 /// publicinterfaceIMethodInterceptor { //////调用拦截器 /// ///拦截的目标方法 /// 拦截的目标方法参数列表 /// 拦截的目标方法返回值 objectInterceptor(MethodInfotargetMethod,object[]args); } //////代理类生成器 /// publicclassProxyFactory:DispatchProxy { privateIMethodInterceptor_interceptor; //////创建代理类实例 /// ///要代理的接口 /// 拦截器 /// publicstaticobjectCreateInstance(TypetargetType,IMethodInterceptorinterceptor) { objectproxy=GetProxy(targetType); ((ProxyFactory)proxy).GetInterceptor(interceptor); returnproxy; } /// ///创建代理类实例 /// ///要代理的接口 /// 拦截器 /// 拦截器构造函数参数值 /// 代理实例 publicstaticobjectCreateInstance(TypetargetType,TypeinterceptorType,paramsobject[]parameters) { objectproxy=GetProxy(targetType); ((ProxyFactory)proxy).GetInterceptor(interceptorType,parameters); returnproxy; } //////创建代理类实例 /// ///要代理的接口 /// 拦截器 /// 拦截器构造函数参数值 /// publicstaticTTargetCreateInstance (paramsobject[]parameters)whereTInterceptor:IMethodInterceptor { objectproxy=GetProxy(typeof(TTarget)); ((ProxyFactory)proxy).GetInterceptor(typeof(TInterceptor),parameters); return(TTarget)proxy; } /// ///获取代理类 /// ////// privatestaticobjectGetProxy(TypetargetType) { MethodCallExpressioncallexp=Expression.Call(typeof(DispatchProxy),nameof(DispatchProxy.Create),new[]{targetType,typeof(ProxyFactory)}); returnExpression.Lambda >(callexp).Compile()(); } /// ///获取拦截器 /// ////// privatevoidGetInterceptor(TypeinterceptorType,object[]parameters) { Type[]ctorParams=parameters.Select(x=>x.GetType()).ToArray(); IEnumerable paramsExp=parameters.Select(x=>Expression.Constant(x)); NewExpressionnewExp=Expression.New(interceptorType.GetConstructor(ctorParams),paramsExp); this._interceptor=Expression.Lambda >(newExp).Compile()(); } /// ///获取拦截器 /// ///privatevoidGetInterceptor(IMethodInterceptorinterceptor) { this._interceptor=interceptor; } /// ///执行代理方法 /// ////// /// protectedoverrideobjectInvoke(MethodInfotargetMethod,object[]args) { returnthis._interceptor.Interceptor(targetMethod,args); } } /// ///表演者 /// publicinterfaceIPerform { //////唱歌 /// voidSing(); //////跳舞 /// voidDance(); } //////具体的表演者——刘德华Andy /// publicclassAndyPerformer:IPerform { publicvoidDance() { System.Console.WriteLine("给大家表演一个舞蹈"); } publicvoidSing() { System.Console.WriteLine("给大家唱首歌"); } } //////经纪人——负责演员的所有活动 /// publicclassPerformAgent:IMethodInterceptor { publicIPerform_perform; publicPerformAgent(IPerformperform) { this._perform=perform; } publicobjectInterceptor(MethodInfotargetMethod,object[]args) { System.Console.WriteLine("各位大佬,要我们家艺人演出清闲联系我"); objectresult=targetMethod.Invoke(this._perform,args); System.Console.WriteLine("各位大佬,表演结束该付钱了"); returnresult; } } publicclassProgram { staticvoidMain(string[]args) { IPerformperform; //perform=ProxyFactory.CreateInstance(newAndyPerformer()); //perform.Sing(); //perform.Dance(); ServiceCollectionserviceDescriptors=newServiceCollection(); serviceDescriptors.AddSingleton (ProxyFactory.CreateInstance (newAndyPerformer())); IServiceProviderserviceProvider=serviceDescriptors.BuildServiceProvider(); perform=serviceProvider.GetService (); perform.Sing(); perform.Dance(); System.Console.ReadKey(); } } }
总结
- 使用拓展方法只能拓展新增方法,不能增强已有的功能
- 使用继承类或接口,类只能单继承,并且在父类改变后,所有的子类都要跟着变动
- 使用代理模式与继承一样代理对象和真实对象之间的的关系在编译时就确定了
- 使用装饰者模式能够在运行时动态地增强类的功能
参考引用
利用.NETCore类库System.Reflection.DispatchProxy实现简易Aop
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。