日常开发中,反射可以说是经常用到的东西了,比如 类属性不能写死的 、List行转列的 、执行方法,调用类可配置之类的等等 都会用到反射。
一、动态判断对象属性值
比如说我需要判断一个对象里面 所有属性的值是否都为true
先创建一个类Temp
public class Temp
{
public bool CalculationCompleted{get;set;}
public bool CollectionCompleted{get;set;}
public bool ConfigCompleted{get;set;}
public bool ExecCompleted{get;set;}
}
常见的写法可能是这样
Temp t=new Temp();
if(t.CalculationCompleted&&t.CollectionCompleted&&t.ConfigCompleted&&t.ExecCompleted)
{
Console.WriteLine("所有属性值都为True");
}
这样子写没有毛病, 但是如果要判断的属性变多了呢,比如几十个,那么用这种方法写显而易见,代码量很大,而且只要Temp的属性增加了,就需要重新修改if判断,很不灵活。
下面换一种写法
public static bool IsAllCompleted<T>(T obj)
{
if (obj == null)
{
return false;
}
Type t = typeof(T);
//获取属性的集合
PropertyInfo[] properties = t.GetProperties();
foreach (var p in properties)
{
if (p.Name.Contains("Completed"))
{
//获取属性值
bool isCollectionCompleted = (bool)p.GetValue(obj, null);
if (!isCollectionCompleted)
{
//只要有一个数据为false就直接返回
return false;
}
}
}
return true;
}
Temp t=new Temp();
if(IsAllCompleted<Temp>(t))
{
Console.WriteLine("所有属性值都为True");
}
这种写法通过反射获取对象中所有属性,并获取值,然后循环判断值是否为false,显而易见,通过反射不需要管类中有多少个属性,不管是新增的属性还是删除的属性 ,只需要一个循环就可以得到所有属性的值,可以说非常灵活。
二、动态生成Sql
下面写一个我们经常写的Sql语句
Insert into Temp(CalculationCompleted,CollectionCompleted,ConfigCompleted,ExecCompleted) Values(@CalculationCompleted,@CollectionCompleted,@ConfigCompleted,@ExecCompleted)
这是一个参数化的插入Sql,如果表字段比较少,那这么写还好,但是如果很多,而且表字段不固定,那么这么写就不灵活了。
下面通过运用反射来动态生成Sql
首先需要这个表的实体类,我们还用这个吧
public class Temp
{
public bool CalculationCompleted{get;set;}
public bool CollectionCompleted{get;set;}
public bool ConfigCompleted{get;set;}
public bool ExecCompleted{get;set;}
}
封装两个方法
/// <summary>
/// 返回属性名称(name,name,name)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public string GetParas<T>()
{
StringBuilder columns = new StringBuilder();
Type t = typeof(T);
PropertyInfo[] properties = t.GetProperties();
foreach (var p in properties)
{
if (p.PropertyType.Name == "IList`1" || p.PropertyType.Name.ToUpper().Contains("LIST"))
{
continue;
}
columns.Append(p.Name).Append(",");
}
return columns.Remove(columns.Length - 1, 1).ToString();
}
/// <summary>
/// 返回属性名称(@name,@name,@name)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public string GetATParas<T>()
{
StringBuilder columns = new StringBuilder();
Type t = typeof(T);
PropertyInfo[] properties = t.GetProperties();
foreach (var p in properties)
{
if (p.PropertyType.Name == "IList`1" || p.PropertyType.Name.ToUpper().Contains("LIST"))
{
continue;
}
columns.Append("@").Append(p.Name).Append(",");
}
return columns.Remove(columns.Length - 1, 1).ToString();
}
调用方法
string para=GetParas<Temp>();
string atPara=GetATParas<Temp>();
Insert into Temp(para) Values(atPara)
这样写,如果Temp表中新增的字段,那么只要实体类重新生成一下就可以了,sql语句完全不需要修改。
三、动态调用类中的方法
先创建一个类
public class Temp
{
public bool CalculationCompleted{get;set;}
public bool CollectionCompleted{get;set;}
public bool ConfigCompleted{get;set;}
public bool ExecCompleted{get;set;}
public bool Calculation(DateTime time)
{
return true;
}
public bool Collection(DateTime time)
{
return true;
}
public bool Config(DateTime time)
{
return true;
}
public bool Exec(DateTime time)
{
return true;
}
}
常见的调用类方法
Temp t=new Temp();
DateTime time=new DateTime();
t.CalculationCompleted=t.Calculation(time);
t.CollectionCompleted=t.Collection(time);
t.ConfigCompleted=t.Config(time);
t.ExecCompleted=t.Exec(time);
这样子明显很不灵活。如果类成员属性、方法增加就需要在重新写N条t.XXXCompleted=t.XXX(time)
反射执行类方法
public static void ReflectExecMethod<T>(T obj)
{
string strReslutMsg = string.Empty;
decimal reslutMoney = 0;
Type t = typeof(T);
PropertyInfo[] properties = t.GetProperties();
foreach (var p in properties)
{
//获取属性值
bool isCollectionCompleted = (bool)p.GetValue(obj, null);
//判断属性值是否为false,如果是则执行方法
if (!isCollectionCompleted)
{
//方法需要的参数集合
object[] args = new object[] { DateTime.Now };
//获取方法名
string strMethodName = p.Name.Split(new string[] { "Completed" }, StringSplitOptions.RemoveEmptyEntries)[0];
//执行方法得到结果
bool result = (bool)t.GetMethod(strMethodName).Invoke(obj, args);
//赋值
p.SetValue(obj, result, null);
}
}
调用
ReflectExecMethod<Temp>(t)
通过反射调用类成员方法,并赋值 。。这样子写的好处,如果类成员方法、成员属性后面在增加了,主要符合规则,上面的代码就不需要修改。
四、行转列
创建一个最终生成表格的实体类
public class TempRowToColumn
{
//类目
public string Category{get;set;}
//查了几天 这里就要写几个属性
public stirng Time1{get;set;}
public stirng Time2{get;set;}
public stirng Time3{get;set;}
public stirng Time4{get;set;}
public stirng Time5{get;set;}
}
表格如下
我们的实体类
public class Temp
{
public bool CalculationCompleted{get;set;}
public bool CollectionCompleted{get;set;}
public bool ConfigCompleted{get;set;}
public bool ExecCompleted{get;set;}
}
查询出的列表
很明显我们列表的列变成了表格的行
下面我们用反射完成行转列的操作
//创建一个存储最终生成表格的List
List<TempRowToColumn> tempRowToColumnList=new List<TempRowToColumn>();
//先把要显示的所有列存进去
TempRowToColumn t=new TempRowToColumn();
t.Category="计算是否完成";
tempRowToColumnList.add(t);
TempRowToColumn t=new TempRowToColumn();
t.Category="采集是否完成";
tempRowToColumnList.add(t);
TempRowToColumn t=new TempRowToColumn();
t.Category="配置是否完成";
tempRowToColumnList.add(t);
TempRowToColumn t=new TempRowToColumn();
t.Category="执行是否完成";
tempRowToColumnList.add(t);
//创建一个存储未进行行转列的集合
List<Temp> tempList=new List<Temp>();
//存储5天的数据
for(int i=0;i<5;i++)
{
Temp t=new Temp();
t.CalculationCompleted=true;
t.CollectionCompleted=true;
t.ConfigCompleted=true;
t.ExecCompleted=true;
t.DateTime=DateTime.Now.AddDate(i);
tempList.add(t);
}
tempRowToColumnList.ForEach(g=>{
//因为TempRowToColumn类第一个属性为类目 所有从所有1开始
int i=1;
//反射获取属性列表
System.Reflection.PropertyInfo[] proInfoArray = g.GetType().GetProperties();
//行转列后 列为时间,所有这里要循环时间
for (DateTime j = tempList.Min(t=>t.DateTime); j <= tempList.Max(t=>t.DateTime); j = j.AddDays(1))
{
//查找出当前循环到日期的一条数据
Temp tt=tempList.find(t=>t.DateTime.CompareTo(j)==0);
bool value=false;
switch(g.Category)
{
case "计算是否完成":value=tt.CalculationCompleted;
break;
case "采集是否完成":value=tt.CollectionCompleted;
break;
case "配置是否完成":value=tt.ConfigCompleted;
break;
case "执行是否完成":value=tt.ExecCompleted;
break;
default:value=false;
break;
}
string strValue=value??"是":"否";
proInfoArray[i].SetValue(g,strValue,null);
i++;
}
})
执行结束后 tempRowToColumnList就是行转列后的结果集。
结束语:
上面仅仅是反射一部分的日常开发经常用到的使用方法,反射的用处很多,我就不一一写了,请自行开发反射用途
上面的代码都为Notepad++ 手打,所以可能有失误