C# 自定义特性Attribute与反射调用
前言
👨💻👨🌾📝记录学习成果,以便温故而知新
一个项目有对个打印任务,每个打印任务调用不同的Excel模板进行打印。利用自定义特性PrintTaskAttribute来标识打印任务函数,再利用反射的方法把打印任务名与打印方法的映射关系存到字典里。以下是把项目中的思路抽出来整了个演示项目。
项目结构如下:
1.AttributeUtil.cs
文件中含AttributeUtil(特性工具类)、PrintTaskAttribute(打印任务特性)与ObjectMethodInfo(特性内容与映射方法类)。AttributeUtil工具类有MapPrintTaskToMethod与InvokePrintTask两个方法,MapPrintTaskToMethod方法映射程序集中所有含打印任务特性的方法,InvokePrintTask调用映射的打印任务方法。
namespace Print
{
/// <summary>
/// 特性工具类
/// <para>author: Antonio</para>
/// </summary>
class AttributeUtil
{
/// <summary>
/// 打印任务映射处理方法
/// <para>author: Antonio</para>
/// </summary>
/// <param name="objectMethodDictionary">打印任务与调用方法映射字典</param>
/// <param name="assemblyName">程序集</param>
public void MapPrintTaskToMethod(out Dictionary<string, ObjectMethodInfo> objectMethodDictionary, string assemblyName)
{
objectMethodDictionary = new Dictionary<string, ObjectMethodInfo>();
Assembly assembly = Assembly.Load(assemblyName);
Type[] types = assembly.GetTypes();
foreach (Type t in types)
{
object _Object = null;
//Debug.WriteLine(t.FullName, "类名");
MethodInfo[] methodInfos = t.GetMethods();
foreach (MethodInfo methodInfo in methodInfos)
{
//Debug.WriteLine(methodInfo.Name, "方法名");
IEnumerable<Attribute> attributes = methodInfo.GetCustomAttributes();
foreach (Attribute attr in attributes)
{
//Debug.WriteLine(attr.ToString(), "特性");
if (attr is PrintTaskAttribute)
{
PrintTaskAttribute printTaskAttribute = (PrintTaskAttribute)attr;
string printTaskName = printTaskAttribute.printTaskName;
//Debug.WriteLine(printTaskName, "PrintTaskName");
if (!objectMethodDictionary.ContainsKey(printTaskName))
{
ObjectMethodInfo objectMethod = new ObjectMethodInfo();
if (_Object == null)
{
_Object = Activator.CreateInstance(t);
}
objectMethod._Object = _Object;//类
objectMethod._MethodInfo = methodInfo;//方法
objectMethodDictionary[printTaskName] = objectMethod;
}
else
{
Debug.Fail(t.FullName + "." + methodInfo.Name + "[" + printTaskAttribute.TypeId + "(PrintTaskName)]:" + printTaskName + "已存在");
}
}
}
}
}
}
/// <summary>
/// 调用打印任务
/// <para>author: Antonio</para>
/// </summary>
/// <param name="objectMethodDictionary">打印任务与调用方法映射字典</param>
/// <param name="printTaskName">打印任务</param>
/// <param name="dic">参数</param>
/// <returns></returns>
public bool InvokePrintTask(Dictionary<string, ObjectMethodInfo> objectMethodDictionary, string printTaskName, Dictionary<string, Object> dic)
{
bool b = false;
if (objectMethodDictionary.ContainsKey(printTaskName))
{
object o = objectMethodDictionary[printTaskName]._MethodInfo.Invoke(objectMethodDictionary[printTaskName]._Object, new object[] { dic });
b = (bool)o;
}
return b;
}
}
/// <summary>
/// 打印任务特性
/// <para>author: Antonio</para>
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class PrintTaskAttribute : Attribute
{
public string printTaskName { get; set; }
public PrintTaskAttribute(string printTaskName)
{
this.printTaskName = printTaskName;
}
}
/// <summary>
/// 特性内容与映射方法类
/// <para>author: Antonio</para>
/// </summary>
public class ObjectMethodInfo
{
/// <summary>
/// 类
/// </summary>
public object _Object { get; set; }
/// <summary>
/// 类方法
/// </summary>
public MethodInfo _MethodInfo { get; set; }
}
}
核心代码:
object o = objectMethodDictionary[printTaskName]._MethodInfo.Invoke(objectMethodDictionary[printTaskName]._Object, new object[] { dic });
objectMethodDictionary[printTaskName]._MethodInfo是调用的方法。
参数objectMethodDictionary[printTaskName]._Object是方法所属对象。
new object[] { dic }是传参。
2.两个打印任务类
(1)PrintTaskA.cs
模拟打印任务A下有个模板
namespace Print
{
/// <summary>
/// 打印任务A
/// <para>author: Antonio</para>
/// </summary>
class PrintTaskA
{
/// <summary>
/// 任务A-模板1
/// <para>author: Antonio</para>
/// </summary>
/// <param name="dic">参数</param>
/// <returns></returns>
[PrintTask("任务A-模板1")]
public bool Task_A_1(Dictionary<string, Object> dic)
{
Console.WriteLine("===任务A-模板1===");
Console.WriteLine("姓名:" + dic["name"]);
Console.WriteLine("年龄" + dic["age"]);
return true;
}
/// <summary>
/// 任务A-模板2
/// <para>author: Antonio</para>
/// </summary>
/// <param name="dic">参数</param>
/// <returns></returns>
[PrintTask("任务A-模板2")]
public bool Task_A_2(Dictionary<string, Object> dic)
{
Console.WriteLine("===任务A-模板2===");
Console.Write("姓名:" + dic["name"]);
Console.Write(" ");
Console.WriteLine("年龄" + dic["age"]);
return true;
}
}
}
(2)PrintTaskB.cs
模拟打印任务B下有3个模板
namespace Print
{
/// <summary>
/// 打印任务B
/// <para>author: Antonio</para>
/// </summary>
class PrintTaskB
{
/// <summary>
/// 任务B-模板1
/// <para>author: Antonio</para>
/// </summary>
/// <param name="dic">参数</param>
/// <returns></returns>
[PrintTask("任务B-模板1")]
public bool Task_B_1(Dictionary<string, Object> dic)
{
Console.WriteLine("===任务B-模板1===");
Console.WriteLine(dic["Tom"] + "和" +dic["Jerry"] + "一起工作");
return true;
}
/// <summary>
/// 任务B-模板2
/// <para>author: Antonio</para>
/// </summary>
/// <param name="dic">参数</param>
/// <returns></returns>
[PrintTask("任务B-模板2")]
public bool Task_B_2(Dictionary<string, Object> dic)
{
Console.WriteLine("===任务B-模板1===");
Console.WriteLine(dic["Tom"] + "帮助" + dic["Jerry"] );
return true;
}
/// <summary>
/// 任务B-模板3
/// <para>author: Antonio</para>
/// </summary>
/// <param name="dic">参数</param>
/// <returns></returns>
[PrintTask("任务B-模板3")]
public bool Task_B_3(Dictionary<string, Object> dic)
{
Console.WriteLine("===任务B-模板1===");
Console.WriteLine(dic["Jerry"] + "帮助" + dic["Tom"]);
return true;
}
}
}
3.Program.cs
演示效果代码如下:
namespace Print
{
class Program
{
static void Main(string[] args)
{
Dictionary<string, ObjectMethodInfo> objectMethodDictionary = new Dictionary<string, ObjectMethodInfo>();
AttributeUtil util = new AttributeUtil();
//程序集Print中打印任务映射处理方法
util.MapPrintTaskToMethod(out objectMethodDictionary, "Print");
Dictionary<string, Object> dic1 = new Dictionary<string, object>();
dic1["name"] = "Tom";
dic1["age"] = 18;
util.InvokePrintTask(objectMethodDictionary, "任务A-模板1", dic1);//调用任务A-模板1
util.InvokePrintTask(objectMethodDictionary, "任务A-模板2", dic1);//调用任务A-模板3
Dictionary<string, Object> dic2 = new Dictionary<string, object>();
dic2["Tom"] = "Tom";
dic2["Jerry"] = "Jerry";
util.InvokePrintTask(objectMethodDictionary, "任务B-模板1", dic2);//调用任务B-模板1
util.InvokePrintTask(objectMethodDictionary, "任务B-模板2", dic2);//调用任务B-模板2
util.InvokePrintTask(objectMethodDictionary, "任务B-模板3", dic2);//调用任务B-模板3
Console.Read();
}
}
}
在实际项目中调用打印任务,打印任务名一般是以动态参数形式传入,dic参数应与打印任务匹配,这样的话如果有新的打印任务,只需新增打印任务功能方法,其它代码可以少改,甚至不需改动。