背景
实际开发中业务和日志尽量不要相互干扰嵌套,否则很难维护和调试。
示例
using System.Reflection;
namespace CSharpLearn
{
internal class Program
{
static void Main()
{
int age = 25;
string name = "bingling";
Person person = new(age, name);
Console.WriteLine("====================不使用AOP====================");
person.DisplayMessage();
Console.WriteLine();
person.DisplayMessage("name");
Console.WriteLine("====================不使用AOP====================");
Console.WriteLine();
/*============================代理对象============================*/
PersonProxyDynamic<Person> personProxyDynamic = new();
personProxyDynamic.Before += (methodInfo) =>
{
List<string> pt = new();
foreach (ParameterInfo? parameterInfo in methodInfo.GetParameters())
{
if (parameterInfo.ParameterType.FullName != null)
{
pt.Add(parameterInfo.ParameterType.FullName);
}
}
Console.WriteLine($"准备执行{methodInfo.Name}({string.Join(",", pt)})");
};
personProxyDynamic.After += (methodInfo) =>
{
Console.WriteLine($"执行{methodInfo.Name}完毕");
};
/*============================代理对象============================*/
Console.WriteLine("====================使用了AOP====================");
personProxyDynamic.Excute(person, "DisplayMessage", new object[] { "name" });
Console.WriteLine();
personProxyDynamic.Excute(person, "DisplayMessage", null);
Console.WriteLine();
personProxyDynamic.Excute(person, "DisplayMessage", new object[] { 123 });
Console.WriteLine();
Console.WriteLine("====================使用了AOP====================");
}
}
/// <summary>
/// 人物类
/// </summary>
public class Person
{
/// <summary>
/// 姓名
/// </summary>
private readonly string name;
/// <summary>
/// 年龄
/// </summary>
private readonly int age;
public Person(int age, string name)
{
this.age = age;
this.name = name;
}
/// <summary>
/// 打印姓名和年龄
/// </summary>
public void DisplayMessage()
{
Console.WriteLine($"{{'age':{age},'name':'{name}'}}");
}
/// <summary>
/// 根据需要打印姓名或年龄
/// </summary>
/// <param name="type">传入name或age</param>
public void DisplayMessage(string type)
{
switch (type)
{
case "name":
Console.WriteLine($"{{'name':'{name}'}}");
break;
case "age":
Console.WriteLine($"{{'age':'{age}'}}");
break;
}
}
}
/// <summary>
/// 代理类
/// </summary>
/// <typeparam name="T">泛型T</typeparam>
public class PersonProxyDynamic<T>
{
/// <summary>
/// 执行方法前
/// </summary>
public Action<MethodInfo>? Before { get; set; }
/// <summary>
/// 执行方法后
/// </summary>
public Action<MethodInfo>? After { set; get; }
/// <summary>
/// 代理执行某个对象的某个方法
/// </summary>
/// <param name="t">被代理的对象</param>
/// <param name="method">被执行的方法</param>
/// <param name="args">被执行方法的参数列表</param>
public void Excute(T t, string method, params object[]? args)
{
//获取被代理对象的所有名字为传入method的方法
MethodInfo[]? methodInfos = typeof(T).GetMethods().Where(item => item.Name == method).ToArray();
//没有此名字的方法
if (methodInfos == null || methodInfos.Length == 0 || methodInfos.FirstOrDefault(item => item == null) != null)
{
Console.WriteLine($"{t}对象没有{method}方法");
return;
}
//存在此名字的方法,注意区分是否为重载的方法
foreach (MethodInfo methodInfo in methodInfos)
{
//方法传参列表是否和args每个元素的类型一一对应
bool flag = true;
//无参方法,传入null或长度为0的参数列表都可以
if (methodInfo.GetParameters().Length == 0 && (args == null || args.Length == 0))
{
}
//有参方法,两者传参长度需要一致、且传入的参数类型等于方法的参数类型或者隶属于其子类
else if (methodInfo.GetParameters().Length == args?.Length)
{
for (int i = 0; i < methodInfo.GetParameters().Length; i++)
{
Type type1 = methodInfo.GetParameters()[i].ParameterType;
Type type2 = args[i].GetType();
//参数列表类型不一致,且传入的参数类型不隶属于签名的参数类型
if (type1 != type2 && !type2.IsSubclassOf(type1))
{
flag = false;
break;
}
}
}
//有参方法,长度不一致
else
{
flag = false;
}
//命中用户想调用的方法
if (flag)
{
Before?.Invoke(methodInfo);
methodInfo.Invoke(t, args);
After?.Invoke(methodInfo);
return;
}
}
Console.WriteLine("未找到您要调用的方法");
}
}
}
运行结果
解析
业务中有一个Person类,其业务为调用Person实例的DisplayMessage方法打印该person对象的个人信息。
PersonProxyDynamic类为其动态代理类,我们在给其对象添加前置后置处理函数后,即可对person对象的所有方法进行代理,这样我们就可以在原本使用person对象方法的地方,替换成动态代理类的Execute方法,保证的原有的person代码不变,业务逻辑干净纯粹。
此示例仅供参考,由于使用了反射,生产环境若是对性能具备高要求,切勿轻易使用!!!