C#中关于增强类功能的几种方式
C#中关于增强类功能的几种方式
本文主要讲解如何利用C#语言自身的特性来对一个类的功能进行丰富与增强,便于拓展现有项目的一些功能。
拓展方法
扩展方法被定义为静态方法,通过实例方法语法进行调用。方法的第一个参数指定该方法作用于哪个类型,并且该参数以 this 修饰符为前缀。仅当使用 using 指令将命名空间显式导入到源代码中之后,扩展方法才可使用。
namespace Extensions
{
public static class StringExtension
{
public static DateTime ToDateTime(this string source)
{
DateTime.TryParse(source, out DateTime result);
return result;
}
}
}
注意:
- 如果扩展方法与该类型中定义的方法具有相同的签名,则扩展方法永远不会被调用。
- 在命名空间级别将扩展方法置于相应的作用范围内。例如,在一个名为 Extensions 的命名空间中具有多个包含扩展方法的静态类,则在使用这些拓展方法时,必须引用其命名空间 using Extensions
继承
继承 面向对象的一个特性,属于Is a 关系,比如说Student继承Person,则说明Student is a Person。子类可以通过重写父类的方法或添加新的方法来实现对父类的拓展。
namespace Inherit
{
public class Persion
{
public string Name { get; set; }
public int Age { get; set; }
public void Eat()
{
Console.WriteLine("吃饭");
}
public void Sleep()
{
Console.WriteLine("睡觉");
}
}
public class Student : Persion
{
public void Study()
{
Console.WriteLine("学习");
}
public new void Sleep()
{
Console.WriteLine("做作业,复习功课");
base.Sleep();
}
}
}
继承的缺点:
- 父类的内部细节对子类是可见的
- 子类与父类的继承关系在编译阶段就确定下来了,无法在运行时动态改变从父类继承方法的行为
- 如果父类方法做了修改,所有的子类都必须做出相应的调整,子类与父类是一种高度耦合,违反了面向对象的思想。
组合
组合就是在设计类的时候把需要用到的类作为成员变量加入到当前类中。
组合的优缺点:
- 优点:
- 隐藏了被引用对象的内部细节
- 降低了两个对象之间的耦合
- 可以在运行时动态修改被引用对象的实例
- 缺点:
- 系统变更可能需要不停的定义新的类
- 系统结构变复杂,不再局限于单个类
建议多使用组合,少用继承
装饰者模式
装饰者模式指在不改变原类定义及继承关系的情况跟下,动态的拓展一个类的功能,就是利用创建一个包装类(wrapper)来装饰(decorator)一个已有的类。
包含角色:
- 被装饰者:
- Component 抽象被装饰者,
- ConcreteComponent 具体被装饰者,Component的实现,在装饰者模式中装饰的就是这货。
- 装饰者:
- Decorator 装饰者 一般是一个抽象类并且作为Component的子类,Decorator必然会有一个成员变量用来存储Component的实例
- ConcreateDecorator 具体装饰者 Decorator的实现
在装饰者模式中必然会有一个最基本,最核心,最原始的接口或抽象类充当component和decorator的抽象组件
实现要点:
- 定义一个类或接口,并且让装饰者及被装饰者的都继承或实现这个类或接口
- 装饰者中必须持有被装饰者的引用
- 装饰者中对需要增强的方法进行增强,不需要增强的方法调用原来的业务逻辑
namespace Decorator
{
/// <summary>
/// Component 抽象者装饰者
/// </summary>
public interface IStudent
{
void Learn();
}
/// <summary>
/// ConcreteComponent 具体被装饰者
/// </summary>
public class Student : IStudent
{
private string _name;
public Student(string name)
{
this._name = name;
}
public void Learn()
{
System.Console.WriteLine(this._name + "学习了以上内容");
}
}
/// <summary>
/// Decorator 装饰者
/// </summary>
public abstract class Teacher : IStudent
{
private IStudent _student;
public Teacher(IStudent student)
{
this._student = student;
}
public virtual void Learn()
{
this.Rest();
this._student.Learn();
}
public virtual void Rest()
{
Console.WriteLine("课间休息");
}
}
/// <summary>
/// ConcreteDecorator 具体装饰者
/// </summary>
public class MathTeacher : Teacher
{
private String _course;
public MathTeacher(IStudent student, string course) : base(student)
{
this._course = course;
}
public override void Learn()
{
System.Console.WriteLine("学习新内容:" + this._course);
base.Learn();
}
public override void Rest()
{
System.Console.WriteLine("课间不休息,开始考试");
}
}
/// <summary>
/// ConcreteDecorator 具体装饰者
/// </summary>
public class EnlishTeacher : Teacher
{
private String _course;
public EnlishTeacher(IStudent student, string course) : base(student)
{
this._course = course;
}
public override void Learn()
{
this.Review();
System.Console.WriteLine("学习新内容:" + this._course);
base.Learn();
}
public void Review()
{
System.Console.WriteLine("复习英文单词");
}
}
public class Program
{
static void Main(string[] args)
{
IStudent student = new Student("student");
student = new MathTeacher(student, "高数");
student = new EnlishTeacher(student, "英语");
student.Learn();
}
}
}
装饰者模式优缺点:
- 优点:
- 装饰者与被装饰者可以独立发展,不会互相耦合
- 可以作为继承关系的替代方案,在运行时动态拓展类的功能
- 通过使用不同的装饰者类或不同的装饰者排序,可以得到各种不同的结果
- 缺点:
- 产生很多装饰者类
- 多层装饰复杂
代理模式
代理模式就是给一个对象提供一个代理对象,并且由代理控制原对象的引用。
包含角色:
- 抽象角色:抽象角色是代理角色和被代理角色的所共同继承或实现的抽象类或接口
- 代理角色:代理角色是持有被代理角色引用的类,代理角色可以在执行被代理角色的操作时附加自己的操作
- 被代理角色:被代理角色是代理角色所代理的对象,是真实要操作的对象
静态代理
动态代理涉及到反射技术相对静态代理会复杂很多,掌握好动态代理对AOP技术有很大帮助
namespace Proxy
{
/// <summary>
/// 共同抽象角色
/// </summary>
public interface IBuyHouse
{
void Buy();
}
/// <summary>
/// 真实买房人,被代理角色
/// </summary>
public class Customer : IBuyHouse
{
public void Buy()
{
System.Console.WriteLine("买房子");
}
}
/// <summary>
/// 中介-代理角色
/// </summary>
public class CustomerProxy : IBuyHouse
{
private IBuyHouse target;
public CustomerProxy(IBuyHouse buyHouse)
{
this.target = buyHouse;
}
public void Buy()
{
System.Console.WriteLine("筛选符合条件的房源");
this.target.Buy();
}
}
public class Program
{
static void Main(string[] args)
{
IBuyHouse buyHouse = new CustomerProxy(new Customer());
buyHouse.Buy();
System.Console.ReadKey();
}
}
}
动态代理
namespace DynamicProxy
{
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
/// <summary>
/// 方法拦截器接口
/// </summary>
public interface IMethodInterceptor
{
/// <summary>
/// 调用拦截器
/// </summary>
/// <param name="targetMethod">拦截的目标方法</param>
/// <param name="args">拦截的目标方法参数列表</param>
/// <returns>拦截的目标方法返回值</returns>
object Interceptor(MethodInfo targetMethod, object[] args);
}
/// <summary>
/// 代理类生成器
/// </summary>
public class ProxyFactory : DispatchProxy
{
private IMethodInterceptor _interceptor;
/// <summary>
/// 创建代理类实例
/// </summary>
/// <param name="targetType">要代理的接口</param>
/// <param name="interceptor">拦截器</param>
/// <returns></returns>
public static object CreateInstance(Type targetType, IMethodInterceptor interceptor)
{
object proxy = GetProxy(targetType);
((ProxyFactory)proxy).GetInterceptor(interceptor);
return proxy;
}
/// <summary>
/// 创建代理类实例
/// </summary>
/// <param name="targetType">要代理的接口</param>
/// <param name="interceptorType">拦截器</param>
/// <param name="parameters">拦截器构造函数参数值</param>
/// <returns>代理实例</returns>
public static object CreateInstance(Type targetType, Type interceptorType, params object[] parameters)
{
object proxy = GetProxy(targetType);
((ProxyFactory)proxy).GetInterceptor(interceptorType, parameters);
return proxy;
}
/// <summary>
/// 创建代理类实例
/// </summary>
/// <typeparam name="TTarget">要代理的接口</typeparam>
/// <typeparam name="TInterceptor">拦截器</typeparam>
/// <param name="parameters">拦截器构造函数参数值</param>
/// <returns></returns>
public static TTarget CreateInstance<TTarget, TInterceptor>(params object[] parameters) where TInterceptor : IMethodInterceptor
{
object proxy = GetProxy(typeof(TTarget));
((ProxyFactory)proxy).GetInterceptor(typeof(TInterceptor), parameters);
return (TTarget)proxy;
}
/// <summary>
/// 获取代理类
/// </summary>
/// <param name="targetType"></param>
/// <returns></returns>
private static object GetProxy(Type targetType)
{
MethodCallExpression callexp = Expression.Call(typeof(DispatchProxy), nameof(DispatchProxy.Create), new[] { targetType, typeof(ProxyFactory) });
return Expression.Lambda<Func<object>>(callexp).Compile()();
}
/// <summary>
/// 获取拦截器
/// </summary>
/// <param name="interceptorType"></param>
/// <param name="parameters"></param>
private void GetInterceptor(Type interceptorType, object[] parameters)
{
Type[] ctorParams = parameters.Select(x => x.GetType()).ToArray();
IEnumerable<ConstantExpression> paramsExp = parameters.Select(x => Expression.Constant(x));
NewExpression newExp = Expression.New(interceptorType.GetConstructor(ctorParams), paramsExp);
this._interceptor = Expression.Lambda<Func<IMethodInterceptor>>(newExp).Compile()();
}
/// <summary>
/// 获取拦截器
/// </summary>
/// <param name="interceptor"></param>
private void GetInterceptor(IMethodInterceptor interceptor)
{
this._interceptor = interceptor;
}
/// <summary>
/// 执行代理方法
/// </summary>
/// <param name="targetMethod"></param>
/// <param name="args"></param>
/// <returns></returns>
protected override object Invoke(MethodInfo targetMethod, object[] args)
{
return this._interceptor.Interceptor(targetMethod, args);
}
}
/// <summary>
/// 表演者
/// </summary>
public interface IPerform
{
/// <summary>
/// 唱歌
/// </summary>
void Sing();
/// <summary>
/// 跳舞
/// </summary>
void Dance();
}
/// <summary>
/// 具体的表演者——刘德华 Andy
/// </summary>
public class AndyPerformer : IPerform
{
public void Dance()
{
System.Console.WriteLine("给大家表演一个舞蹈");
}
public void Sing()
{
System.Console.WriteLine("给大家唱首歌");
}
}
/// <summary>
/// 经纪人——负责演员的所有活动
/// </summary>
public class PerformAgent : IMethodInterceptor
{
public IPerform _perform;
public PerformAgent(IPerform perform)
{
this._perform = perform;
}
public object Interceptor(MethodInfo targetMethod, object[] args)
{
System.Console.WriteLine("各位大佬,要我们家艺人演出清闲联系我");
object result = targetMethod.Invoke(this._perform, args);
System.Console.WriteLine("各位大佬,表演结束该付钱了");
return result;
}
}
public class Program
{
static void Main(string[] args)
{
IPerform perform;
//perform = ProxyFactory.CreateInstance<IPerform, PerformAgent>(new AndyPerformer());
//perform.Sing();
//perform.Dance();
ServiceCollection serviceDescriptors = new ServiceCollection();
serviceDescriptors.AddSingleton<IPerform>(ProxyFactory.CreateInstance<IPerform, PerformAgent>(new AndyPerformer()));
IServiceProvider serviceProvider = serviceDescriptors.BuildServiceProvider();
perform = serviceProvider.GetService<IPerform>();
perform.Sing();
perform.Dance();
System.Console.ReadKey();
}
}
}
总结
- 使用拓展方法只能拓展新增方法,不能增强已有的功能
- 使用继承类或接口,类只能单继承,并且在父类改变后,所有的子类都要跟着变动
- 使用代理模式与继承一样代理对象和真实对象之间的的关系在编译时就确定了
- 使用装饰者模式能够在运行时动态地增强类的功能
参考引用
利用.NET Core类库System.Reflection.DispatchProxy实现简易Aop
C#中关于增强类功能的几种方式的更多相关文章
- C# 中一些类关系的判定方法 C#中关于增强类功能的几种方式 Asp.Net Core 轻松学-多线程之取消令牌
1. IsAssignableFrom实例方法 判断一个类或者接口是否继承自另一个指定的类或者接口. public interface IAnimal { } public interface ID ...
- 在一个web 应用中,改变url无非是2种方式,一种是利用超链接进行跳转,另一种是使用浏览器的前进和回退功能
在一个web 应用中,改变url无非是2种方式,一种是利用超链接进行跳转,另一种是使用浏览器的前进和回退功能 https://www.jianshu.com/p/27ee7df4ccc1
- Struts2中获取HttpServletRequest,HttpSession等的几种方式
转自:http://www.kaifajie.cn/struts/8944.html package com.log; import java.io.IOException; import java. ...
- Mybatis中使用association进行关联的几种方式
这里以一对一单向关联为例.对使用或不使用association的配置进行举例. 实体类: @Data @ToString @NoArgsConstructor public class IdCard ...
- 【Java8新特性】面试官问我:Java8中创建Stream流有哪几种方式?
写在前面 先说点题外话:不少读者工作几年后,仍然在使用Java7之前版本的方法,对于Java8版本的新特性,甚至是Java7的新特性几乎没有接触过.真心想对这些读者说:你真的需要了解下Java8甚至以 ...
- Action中取得request,session的四种方式
Action中取得request,session的四种方式 在Struts2中,从Action中取得request,session的对象进行应用是开发中的必需步骤,那么如何从Action中取得这些对象 ...
- WPF中使用文件浏览对话框的几种方式
原文:WPF中使用文件浏览对话框的几种方式 WPF本身并没有为我们提供文件浏览的控件, 也不能直接使用Forms中的控件,而文件浏览对话框又是我们最常用的控件之一. 下面是我实现的方式 方式1: 使用 ...
- Dojo初探之2:设置dojoConfig详解,dojoConfig参数详解+Dojo中预置自定义AMD模块的四种方式(基于dojo1.11.2)
Dojo中想要加载自定义的AMD模块,需要先设置好这个模块对应的路径,模块的路径就是这个模块的唯一标识符. 一.dojoConfig参数设置详解 var dojoConfig = { baseUrl: ...
- 转 Velocity中加载vm文件的三种方式
Velocity中加载vm文件的三种方式 velocitypropertiespath Velocity中加载vm文件的三种方式: 方式一:加载classpath目录下的vm文件 Prope ...
随机推荐
- MySQL数据库25条规范解读
一.基础规范 (1)必须使用UTF8字符集 解读:万国码,无需转码,无乱码风险,节省空间(由于移动设备原因最好使用utf8mb4) (2)禁止使用存储过程.视图.触发器.Event 解读:高并发大数据 ...
- centos7的防火墙配置
centos7 不在使用iptables作为防火墙, 而是使用firewalld规则, 好吃是支持动态更新, 不需要重启服务, 第二个就是加入了zone概念. 所以和centos6在防火墙配置上有很大 ...
- spring整合elasticsearch之环境搭建
推荐一个非常好的博客: 点我 // 测试使用docker下启动的es不管用, 在linux下或者windows下运行的es可用 // 进一步测试docker下启动的es链接时, 开启嗅探也链接不上, ...
- C/C++中的常量到底存在了什么地方
一般来说,基本类型(整型.字符型等)常量会在编译阶段被编译成立即数,占的是代码段的内存.(代码段是只读的,而且不允程序员获取代码段的地址,所以在c++中,尽量不为const分配数据段的内存,但是一旦取 ...
- C语言——<计算>_较大两个数相乘
例题:9876543210*1234567890 的乘积 分析:正常的数据结构已经无法满足这么大的数相乘的结果.只能使用数组来进行操作. 1.两个数都用字符数组来接收. 2.接收后,因为每一位要乘以另 ...
- IDF实验室—不难不易的js加密
查看源代码 <html> <head> <script src="/tpl/wctf/Public/js/lib/jquery.js">< ...
- 有序列表ol和定义列表dl,dt,dd
有序列表是一种讲究排序列表结构,使用<ol>标签定义,其中包含多个<li>列表项目.一般网页设计中,列表结构可以互用有序或者无序类表标签.但是,在强调项目排序栏目中,选用有序列 ...
- javascript Function类型
Function(函数)类型实际上是对象,每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法,由于函数是对象,因此函数名实际上也是一个指向函数对象的指针 声明方式 func ...
- Java基础教程(15)--枚举类型
枚举类型定义了一个枚举值的列表,每个值是一个标识符.例如,下面的语句声明了一个枚举类型,用来表示星期的可能情况: public enum Day { SUNDAY, MONDAY, TUESDAY ...
- 二:Jquery-action
一:dom对象和jq对象 1.对象含义: dom对象:js方法获取元素,将dom对象存储在变量中 jq对象:jq方法获取元素的jq对象,将jq对象存储在变量中 相互之间不能使用另外一个对象的任何属性和 ...