反射指程序可以访问、检测和修改它本身状态或行为的一种能力。
程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。
您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。
一、 场景
假设有一个类SoftwareRuns用来存放电脑软件的运行状态,最开始的时候,电脑只有三个软件,微信、腾讯QQ和Snipaste截图。
1.1 场景一
/// <summary>
/// 软件运行
/// </summary>
public class SoftwareRuns
{
private bool _isWeChat;
/// <summary>
/// 微信
/// </summary>
[Description("微信")]
public bool IsWeChat
{
get { return _isWeChat; }
private set { _isWeChat = value; }
}
private bool _isQQScLauncher;
/// <summary>
/// 腾讯QQ
/// </summary>
[Description("腾讯QQ")]
public bool IsQQScLauncher
{
get { return _isQQScLauncher; }
private set { _isQQScLauncher = value; }
}
private bool _isSnipaste;
/// <summary>
/// Snipaste截图
/// </summary>
[Description("Snipaste截图")]
public bool IsSnipaste
{
get { return _isSnipaste; }
private set { _isSnipaste = value; }
}
public SoftwareRuns(bool isWeChat,bool isQQScLauncher,bool isSnipaste)
{
this.IsWeChat = IsWeChat;
this.IsQQScLauncher = isQQScLauncher;
this.IsSnipaste = isSnipaste;
}
}
代码段public SoftwareRuns(bool isWeChat,bool isQQScLauncher,bool isSnipaste),在电脑只有三个软件的时候,初始化并没有多大的问题。一旦我后面又加了一堆属性后,就会发现重复的代码增多了。
private bool _isDevenv;
/// <summary>
/// VisualStudio2017
/// </summary>
[Description("VisualStudio2017")]
public bool IsDevenv
{
get { return _isDevenv; }
private set { _isDevenv = value; }
}
private bool _isSsms;
/// <summary>
/// ManagementStudio2012
/// </summary>
[Description("ManagementStudio2012")]
public bool IsSsms
{
get { return _isSsms; }
private set { _isSsms = value; }
}
public SoftwareRuns(bool isWeChat, bool isQQScLauncher, bool isSnipaste, bool isDevenv)
{
this.IsWeChat = IsWeChat;
this.IsQQScLauncher = isQQScLauncher;
this.IsSnipaste = isSnipaste;
this.IsDevenv = isDevenv;
}
这仅仅只是加了一个形参,如果后面电脑安装了N多的软件后,这个类的初始化的形参需要多少个?
1.1.1 原因
类的属性不断增加。
1.1.2 目的
类初始化代码时,减少重复性代码。
1.1.3 方案
为了解决这种现象,早上在论坛里请教大佬们如何缩减代码,最后采用了大佬@Bridge_go的建议,使用字典对类进行实例化。
/// <summary>
/// 通过反射修改属性
/// Dictionary<属性名,值>
/// </summary>
/// <param name="_dic"></param>
public SoftwareRuns(Dictionary<string, bool> _dic)
{
Type type = GetType();
foreach (var item in _dic)
{
PropertyInfo info = type.GetProperty(item.Key);//获取属性
if (info != null)
info.SetValue(this, item.Value, null);//设置属性值
}
}
形式参数Dictionary<string, bool> ,string存放SoftwareRuns的属性名,bool存放SoftwareRuns的属性值。利用放射循环遍历替换SoftwareRuns的值。
SetValue:第一个参数填写将设置其属性值的对象,如果初始化了一个SoftwareRuns对象sr,那么填sr,这里不同,没有传入其他对象,那么用this关键字来代替。
这么一改,初始化对象可能有些啰嗦了,因为都是bool类型的属性,我只在字典添加了为True的值。
1.2 场景二
打开任务管理器类TaskManager,想要看看有哪些程序正在运行。
/// <summary>
/// 输出
/// </summary>
/// <param name="software"></param>
public void Print(SoftwareRuns software)
{
StringBuilder sb = new StringBuilder();
if (software.IsWeChat)
sb.AppendLine("微信运行中!");
if (software.IsQQScLauncher)
sb.AppendLine("腾讯QQ运行中!");
if (software.IsSnipaste)
sb.AppendLine("截图软件运行中!");
Console.WriteLine(sb.ToString());
}
随着软件增多,Print方法将会 变得十分臃肿。
1.2.1 原因
if...else判断过多。
1.2.2 目的
缩减if..else分支,减少重复性代码。
1.2.3 方案
通过PropertyInfo来获取属性的信息。
/// <summary>
/// 输出
/// </summary>
/// <param name="software"></param>
public void Print(SoftwareRuns software)
{
StringBuilder sb = new StringBuilder();
Type type = software.GetType();
foreach (PropertyInfo info in type.GetProperties())
{
bool IsRun = Convert.ToBoolean(info.GetValue(software, null));//获取属性值
if (IsRun)
{
//获取属性注释
string name = info.GetCustomAttributes(typeof(DescriptionAttribute), true).Cast<DescriptionAttribute>().SingleOrDefault().Description;
sb.AppendLine(name + "运行中!");
}
}
Console.WriteLine(sb.ToString());
}
1.3 场景三
某些情况下,if...else判断中,执行方法仅仅只有某个入参值不一样。
/// <summary>
/// 微信安装路径
/// </summary>
private Uri WeChatURI { get { return new Uri(@"D:\WeChat"); } }
/// <summary>
/// 腾讯QQ安装路径
/// </summary>
private Uri QQScLauncherURI { get { return new Uri(@"D:\TencentQQ"); } }
/// <summary>
/// Snipaste截图软件安装路径
/// </summary>
private Uri SnipasteURI { get { return new Uri(@"D:\Snipaste"); } }
/// <summary>
/// 关闭应用程序
/// </summary>
/// <param name="software"></param>
public void Close(SoftwareRuns software)
{
if (software.IsWeChat)
CloseApplication("微信", WeChatURI);
if (software.IsQQScLauncher)
CloseApplication("腾讯QQ",QQScLauncherURI);
if (software.IsSnipaste)
CloseApplication("Snipaste", SnipasteURI);
}
private void CloseApplication(string name,Uri uri)
{
Console.WriteLine("应用程序:" + name + ",已成功关闭!\n路径:" + uri.LocalPath);
}
1.3.1 原因
if...else判断过多。
1.3.2 目的
缩减if..else分支,减少重复性代码。
1.3.3 方案
通过PropertyInfo和Activator来缩减代码。
/// <summary>
/// 关闭应用程序
/// </summary>
/// <param name="software"></param>
public void Close(SoftwareRuns software)
{
Type type = software.GetType();
//命名和URI的规范【格式:info.Name.Substring(2) + "URI"】
foreach (PropertyInfo info in type.GetProperties())
{
if (!Convert.ToBoolean(info.GetValue(software, null))) //应用程序没启动直接跳过
continue;
var function = Activator.CreateInstance<TaskManager>();
//重载的情况下需要指明参数类型
var method = function.GetType().GetMethod("CloseApplication", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, Type.DefaultBinder, new Type[] { typeof(string), typeof(Uri) }, new ParameterModifier[] { new ParameterModifier(2) });
object[] methodPara = new object[2];
methodPara[0] = info.GetCustomAttributes(typeof(DescriptionAttribute), true).Cast<DescriptionAttribute>().SingleOrDefault().Description;
//根据属性名动态获取URI路径
string url = info.Name.Substring(2) + "URI";
PropertyInfo p = GetType().GetProperty(url, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);//避免属性为私有的
if (p == null)
continue;
Uri uri = p.GetValue(this, null) as Uri;
methodPara[1] = uri;
object result = method.Invoke(function, methodPara);
}
}
以上内容只为方便理解。
二、资源链接
百度网盘:https://pan.baidu.com/s/1YWUA--Bb7vdUOo7-mnaIgQ 提取码:wwqp