Preface
This article mainly explains how to use the C# language itself to enrich and enhance the functions of a class, so as to facilitate the expansion of some functions of existing projects.
Expansion method
The extension method is defined as a static method and is called through the instance method syntax. The first parameter of the method specifies which type the method is used for, and the parameter is prefixed with this modifier. Extension methods are only available if the namespace is explicitly imported into the source code using the using directive.
namespace Extensions { public static class StringExtension { public static DateTime ToDateTime(this string source) { (source, out DateTime result); return result; } } }
Notice:
- If the extension method has the same signature as the method defined in that type, the extension method will never be called.
- Place the extension method within the corresponding scope at the namespace level. For example, if you have multiple static classes containing extension methods in a namespace called Extensions, your namespace must be referenced when using Extensions
inherit
Inheritance A feature of object-oriented belongs to the Is a relationship. For example, Student inherits Person, which means that Student is a Person. Subclasses can achieve expansion of parent class by overwriting the methods of the parent class or adding new methods.
namespace Inherit { public class Persion { public string Name { get; set; } public int Age { get; set; } public void Eat() { ("Have a meal"); } public void Sleep() { ("sleep"); } } public class Student : Persion { public void Study() { ("study"); } public new void Sleep() { ("Do homework, review homework"); (); } } }
Disadvantages of inheritance:
- The internal details of the parent class are visible to the subclass
- The inheritance relationship between the child class and the parent class is determined during the compilation stage, and the behavior of inheriting methods from the parent class cannot be dynamically changed at runtime.
- If the parent class method is modified, all subclasses must be adjusted accordingly. The subclass and the parent class are highly coupled, which violates the object-oriented idea.
combination
Combination is to add the required class to the current class as member variables when designing a class.
Pros and cons of combination:
advantage:
- Hide internal details of the referenced object
- Reduces coupling between two objects
- You can dynamically modify instances of referenced objects at runtime
shortcoming:
- System changes may require continuous definition of new classes
- The system structure becomes complex and no longer limited to a single class
It is recommended to use more combinations and less inheritance
Decorator mode
The decorator pattern refers to dynamically expanding the function of a class without changing the original class definition and inheritance relationship, which is to create a wrapper to decorator an existing class.
Includes roles:
Decorated by:
- Component Abstract decorators,
- ConcreteComponent This is the one that is specifically decorated by the decorator, the implementation of Component, in the decorator mode.
Decorator:
- Decorator Decorator is generally an abstract class and is a subclass of Component. Decorator must have a member variable to store instances of Component.
- ConcreateDecorator Decorator Implementation of Decorator
In the decorator pattern, there must be a most basic, core, and primitive interface or abstract class that acts as abstract components of component and decorator
Key points of implementation:
- Define a class or interface and allow both the decorator and the decorator to inherit or implement the class or interface
- The decorator must hold a reference to the decorator
- The decorator enhances the methods that need to be enhanced, and does not require enhanced methods to call the original business logic.
namespace Decorator { /// <summary> /// Component Abstract Decorator /// </summary> public interface IStudent { void Learn(); } /// <summary> /// ConcreteComponent Decorated /// </summary> public class Student : IStudent { private string _name; public Student(string name) { this._name = name; } public void Learn() { (this._name + "Learned the above"); } } /// <summary> /// Decorator Decorator /// </summary> public abstract class Teacher : IStudent { private IStudent _student; public Teacher(IStudent student) { this._student = student; } public virtual void Learn() { (); this._student.Learn(); } public virtual void Rest() { ("Rest between classes"); } } /// <summary> /// ConcreteDecorator Specific Decorator /// </summary> public class MathTeacher : Teacher { private String _course; public MathTeacher(IStudent student, string course) : base(student) { this._course = course; } public override void Learn() { ("Learn new content:" + this._course); (); } public override void Rest() { ("Don't rest during class, start the exam"); } } /// <summary> /// ConcreteDecorator Specific Decorator /// </summary> public class EnlishTeacher : Teacher { private String _course; public EnlishTeacher(IStudent student, string course) : base(student) { this._course = course; } public override void Learn() { (); ("Learn new content:" + this._course); (); } public void Review() { ("Review English Words"); } } public class Program { static void Main(string[] args) { IStudent student = new Student("student"); student = new MathTeacher(student, "High Mathematics"); student = new EnlishTeacher(student, "English"); (); } } }
Advantages and disadvantages of Decorator Mode:
advantage:
- The decorator and the decorator can develop independently and will not be coupled with each other.
- Can be used as an alternative to inheritance relationships to dynamically expand the functions of the class at runtime
- By using different decorators or sorting different decorators, you can get various results
shortcoming:
- Many decorators are produced
- Multi-layer decoration complex
Agent Mode
The proxy mode is to provide an object with a proxy object, and the proxy controls the reference of the original object.
Includes roles:
- Abstract role: An abstract role is an abstract class or interface that is inherited or implemented by the proxy role and the proxy role.
- Agent role: Agent role is a class that is referenced by agent role. Agent role can attach its own operations when executing the operation of the agent role.
- Agent Role: Agent Role is the object that the agent role is proxyed and is the object that is actually to be operated.
Static proxy
Dynamic proxy involves reflection technology, which is much more complicated than static proxy. Mastering dynamic proxy is of great help to AOP technology.
namespace Proxy { /// <summary> /// Shared abstract role /// </summary> public interface IBuyHouse { void Buy(); } /// <summary> /// The real home buyer, the role of being represented /// </summary> public class Customer : IBuyHouse { public void Buy() { ("Buy a house"); } } /// <summary> /// Agent role /// </summary> public class CustomerProxy : IBuyHouse { private IBuyHouse target; public CustomerProxy(IBuyHouse buyHouse) { = buyHouse; } public void Buy() { ("Filter listings that meet the criteria"); (); } } public class Program { static void Main(string[] args) { IBuyHouse buyHouse = new CustomerProxy(new Customer()); (); (); } } }
Dynamic Agent
namespace DynamicProxy { using ; using System; using ; using ; using ; using ; /// <summary> /// Method Interceptor Interceptor /// </summary> public interface IMethodInterceptor { /// <summary> /// Call the interceptor /// </summary> /// <param name="targetMethod">Target method for intercepting</param> /// <param name="args">Intercepted target method parameter list</param> /// <returns>The target method of intercepted return value</returns> object Interceptor(MethodInfo targetMethod, object[] args); } /// <summary> /// Agent class generator /// </summary> public class ProxyFactory : DispatchProxy { private IMethodInterceptor _interceptor; /// <summary> /// Create a proxy class instance /// </summary> /// <param name="targetType">Interface to proxy</param> /// <param name="interceptor">interceptor</param> /// <returns></returns> public static object CreateInstance(Type targetType, IMethodInterceptor interceptor) { object proxy = GetProxy(targetType); ((ProxyFactory)proxy).GetInterceptor(interceptor); return proxy; } /// <summary> /// Create a proxy class instance /// </summary> /// <param name="targetType">Interface to proxy</param> /// <param name="interceptorType">Interceptor</param> /// <param name="parameters">Interceptor constructor parameter value</param> /// <returns>Proxy instance</returns> public static object CreateInstance(Type targetType, Type interceptorType, params object[] parameters) { object proxy = GetProxy(targetType); ((ProxyFactory)proxy).GetInterceptor(interceptorType, parameters); return proxy; } /// <summary> /// Create a proxy class instance /// </summary> /// <typeparam name="TTarget">Interface to proxy</typeparam> /// <typeparam name="TInterceptor">Interceptor</typeparam> /// <param name="parameters">Interceptor constructor parameter value</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> /// Get the proxy class /// </summary> /// <param name="targetType"></param> /// <returns></returns> private static object GetProxy(Type targetType) { MethodCallExpression callexp = (typeof(DispatchProxy), nameof(), new[] { targetType, typeof(ProxyFactory) }); return <Func<object>>(callexp).Compile()(); } /// <summary> /// Get the interceptor /// </summary> /// <param name="interceptorType"></param> /// <param name="parameters"></param> private void GetInterceptor(Type interceptorType, object[] parameters) { Type[] ctorParams = (x => ()).ToArray(); IEnumerable<ConstantExpression> paramsExp = (x => (x)); NewExpression newExp = ((ctorParams), paramsExp); this._interceptor = <Func<IMethodInterceptor>>(newExp).Compile()(); } /// <summary> /// Get the interceptor /// </summary> /// <param name="interceptor"></param> private void GetInterceptor(IMethodInterceptor interceptor) { this._interceptor = interceptor; } /// <summary> /// Execute the proxy method /// </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> /// Performer /// </summary> public interface IPerform { /// <summary> /// Sing /// </summary> void Sing(); /// <summary> /// Dance /// </summary> void Dance(); } /// <summary> /// Specific performer - Andy /// </summary> public class AndyPerformer : IPerform { public void Dance() { ("Perform a dance for everyone"); } public void Sing() { ("Sing a song for everyone"); } } /// <summary> /// Agent - responsible for all activities of the actor /// </summary> public class PerformAgent : IMethodInterceptor { public IPerform _perform; public PerformAgent(IPerform perform) { this._perform = perform; } public object Interceptor(MethodInfo targetMethod, object[] args) { ("Dear guys, please contact me if our artists want to perform leisurely"); object result = (this._perform, args); ("Dear guys, it's time to pay for the show."); return result; } } public class Program { static void Main(string[] args) { IPerform perform; //perform = <IPerform, PerformAgent>(new AndyPerformer()); //(); //(); ServiceCollection serviceDescriptors = new ServiceCollection(); <IPerform>(<IPerform, PerformAgent>(new AndyPerformer())); IServiceProvider serviceProvider = (); perform = <IPerform>(); (); (); (); } } }
Summarize
- Using expansion methods can only expand new methods, and cannot enhance existing functions.
- Using inherited classes or interfaces, classes can only inherit single, and after the parent class changes, all subclasses must change accordingly
- Using proxy mode is the same as inheritance. The relationship between proxy objects and real objects is determined at compile time.
- Use Decorator Mode to dynamically enhance the functionality of the class at runtime
References
Using .NET Core class library to implement simple Aop
Okay, the above is the entire content of this article. I hope that the content of this article has a certain reference value for everyone's study or work. If you have any questions, you can leave a message to communicate. Thank you for your support.