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

原文地址:https://www.cnblogs.com/AlvinLee/p/10180536.html

时间: 2024-10-08 15:48:10

C#中关于增强类功能的几种方式的相关文章

Struts2中获取HttpServletRequest,HttpSession等的几种方式

转自:http://www.kaifajie.cn/struts/8944.html package com.log; import java.io.IOException; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import o

WPF中使用文件浏览对话框的几种方式

原文:WPF中使用文件浏览对话框的几种方式 WPF本身并没有为我们提供文件浏览的控件, 也不能直接使用Forms中的控件,而文件浏览对话框又是我们最常用的控件之一. 下面是我实现的方式 方式1: 使用win32控件OpenFileDialog ? 1 2 3 4 5 6 7 Microsoft.Win32.OpenFileDialog ofd = new Microsoft.Win32.OpenFileDialog(); ofd.DefaultExt = ".xml"; ofd.Fil

Velocity中加载vm文件的三种方式

Velocity中加载vm文件的三种方式: a.  加载classpath目录下的vm文件 Properties p = new Properties(); // 加载classpath目录下的vm文件 // 这里是加载模板VM文件,比如:/META-INF/template/Web_B2CPayment.vm(请参考mas_spring_integration.xml) p.setProperty("file.resource.loader.class", "org.apa

转 Velocity中加载vm文件的三种方式

Velocity中加载vm文件的三种方式 velocitypropertiespath Velocity中加载vm文件的三种方式:   方式一:加载classpath目录下的vm文件 Properties p = new Properties(); p.put("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); Ve

C语言中存储多个字符串的两种方式

C语言中存储多个字符串的两种方式 方式一    二维字符串数组 声明: char name[4][10] = { "Justinian", "Momo", "Becky", "Bush" }; 在内存中的存储: J u s t i n i a n \0 M o m o \0 \0 \0 \0 \0 \0 B e c k y \0 \0 \0 \0 \0 B u s h \0 \0 \0 \0 \0 \0 这种方式会造成内存空间

聊聊业务系统中投递消息到mq的几种方式

背景 电商中有这样的一个场景: 下单成功之后送积分的操作,我们使用mq来实现 下单成功之后,投递一条消息到mq,积分系统消费消息,给用户增加积分 我们主要讨论一下,下单及投递消息到mq的操作,如何实现?每种方式优缺点? 方式一 step1:start transaction step2:生成订单 step3:投递消息到mq step4:commit transaction 这种方式是将发送消息放在了事务提交之前,可能存在的问题: step3发生异常 导致step4失败,下单失败,直接影响到下单业

PHP 中加载类文件的几种方法

PHP 中加载类文件,常用require语句或者include语句.二者的区别是:require语句包含的文件如果有语法错误或者不存在该文件,则会提示错误"fatal error",并且终止程序运行:include语句在同样的情况下,则是提示警告"warning",并且程序继续运行. PHP 加载类文件还可以用require_once或者include_once,他们与require语句或者include语句的区别在于:包含文件时会检查是否已有同样的文件被包含,若是

Js 类定义的几种方式

提起面向对象我们就能想到类,对象,封装,继承,多态.在<javaScript高级程序设计>(人民邮电出版社,曹力.张欣译.英文名字是:Professional JavaScript for Web Developers)这本书中描述的还算比较详细.我们看看JavaScript中定义类的各种方法. 1.工厂方式 javaScript中创建自己的类和对象,我们应该是必须掌握的,我们都知道javaScript中对象的属性可以在对象创建后动态定义,比如下面的代码: <script type=&q

在基于MVC的Web项目中使用Web API和直接连接两种方式混合式接入

在我之前介绍的混合式开发框架中,其界面是基于Winform的实现方式,后台使用Web API.WCF服务以及直接连接数据库的几种方式混合式接入,在Web项目中我们也可以采用这种方式实现混合式的接入方式,虽然Web API或者WCF方式的调用,相对直接连接数据库方式,响应效率上略差一些,不过扩展性强,也可以调动更多的设备接入,包括移动应用接入,网站接入,Winfrom客户端接入,这样可以使得服务逻辑相对独立,负责提供接口即可.这种方式中最有代表性的就是当前Web API的广泛应用,促进了各个接入端