反射获取成员值
这部分主要是为了拿到实例的时候我们可能不清楚实例或成员的类型,但知道起成员的名称或需要拿到其全部成员信息,这个时候就值得用到反射来获取类的详细信息了。
下面显示用反射来获取一个对象某个成员的值。
using System;
using System.Reflection;
public class TestGetMemberValue
{
public static void testMain()
{
object obj = new NewMyDataInfo(123, "测试名字", true);
object itemValue = getMemberValue(obj, "testName");
Console.WriteLine("obj=" + obj + ",testName=" + itemValue.ToString());
}
public static object getMemberValue(object obj, string memberName, BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance)
{
Type type = obj.GetType();
var member = type.GetMember(memberName, bindingFlags);
while (member == null || member.Length == 0)
{
type = type.BaseType;
if (type == null)
return null;
member = type.GetMember(memberName, bindingFlags);
}
switch (member[0].MemberType)
{
case System.Reflection.MemberTypes.Field:
return type.GetField(memberName, bindingFlags).GetValue(obj);
case System.Reflection.MemberTypes.Property:
return type.GetProperty(memberName, bindingFlags).GetValue(obj, null);
default:
return null;
}
}
}
如下为运行打印结果,getMemberValue()可以获取一个对象指定成员变量的值
obj=NewMyDataInfo,testName=测试名字
反射获取未知类型成员内容
如下面两个类是我们实现的原始定义类型
using System.Collections.Generic;
public class DataListInfo
{
public List<MyDataInfo> dataList = new List<MyDataInfo>();
}
public class MyDataInfo
{
public string name = "";
public int number = 0;
public MyDataInfo(string name, int number)
{
this.name = name;
this.number = number;
}
}
该方法用于获得一个测试数据,把本来清晰的结构包成了object,现在要是只给我们object obj对象的话,在不知道它的类型叫DataListInfo的情况下,只知道它有个List成员结构,T是什么类型也不知道。
怎么去拿到这个对象的所有成员数据?
public static object getDataObj()
{
DataListInfo oldData = new DataListInfo();
MyDataInfo info1 = new MyDataInfo("第一个信息", 100);
MyDataInfo info2 = new MyDataInfo("第二个信息", 101);
oldData.dataList.Add(info1);
oldData.dataList.Add(info2);
return oldData;
}
这个时候及UI要用到反射了。
基本想法是根据对象的成员结构逐层处理。这里我没有做详细的扩展,我假定对象的基本成员结构是一个对象列表dataList,其实处理的再全面一些的话,连getMemberValue(“dataList”)都可以省掉的。
如下的代码,就可以实现解析 所有结构跟DataListInfo一致的类,不管List具体是什么类型
using System;
using System.Reflection;
using System.Collections.Generic;
public class Program
{
static void Main(string[] args)
{
//ReflectionBasic.testMain();
object dataInfo = getDataObj();
object dataList = dataInfo.getMemberValue("dataList");
//备注:listCount拿到dataList列表长度
int listCount = System.Convert.ToInt32(dataInfo.getMemberValue("dataList").GetType().InvokeMember("get_Count", BindingFlags.Default | BindingFlags.InvokeMethod, null, dataList, new object[] { }));
//备注:根据列表的0元素去获取列表元素的结构
object dataItem0 = dataList.GetType().InvokeMember("get_Item", BindingFlags.Default | BindingFlags.InvokeMethod, null, dataList, new object[] { 0 });
//备注:诡异的是如果指定调用dataItem0.GetType().GetMembers(BindingFlags.GetField),将什么拿到空成员,所以我只能全部拿来然后再过滤
MemberInfo[] infos = dataItem0.GetType().GetMembers();
List<string> memNameList = new List<string>();
for (int index = 0; index < infos.Length; index++)
{
if (infos[index].MemberType != MemberTypes.Field)
continue;
memNameList.Add(infos[index].Name);
}
//遍历打印dataList所有详细信息
for (int index = 0; index < listCount; index++)
{
//先拿到每个列表对象
object data = dataList.GetType().InvokeMember("get_Item", BindingFlags.Default | BindingFlags.InvokeMethod, null, dataList, new object[] { index });
//打印每个成员信息
string detail = "";
for (int subIndex = 0; subIndex < memNameList.Count; subIndex++)
{
detail = ",字段名=" + memNameList[subIndex] + ",字段值=" + data.getMemberValue(memNameList[subIndex]).ToString() + ";";
}
Console.WriteLine("根节点类型=" + dataInfo.GetType() + ",列表类型=" + dataList.GetType().MakeArrayType() + ",detail=" + detail);
}
Console.ReadLine();
}
public static object getDataObj()
{
DataListInfo oldData = new DataListInfo();
MyDataInfo info1 = new MyDataInfo("第一个信息", 100);
MyDataInfo info2 = new MyDataInfo("第二个信息", 101);
MyDataInfo info3 = new MyDataInfo("第三个信息", 102);
oldData.dataList.Add(info1);
oldData.dataList.Add(info2); ;
oldData.dataList.Add(info3);
return oldData;
}
}
如下为运行打印结果:
根节点类型=DataListInfo,列表类型=System.Collections.Generic.List`1[MyDataInfo][],detail=,字段名=number,字段值=100;
根节点类型=DataListInfo,列表类型=System.Collections.Generic.List`1[MyDataInfo][],detail=,字段名=number,字段值=101;
根节点类型=DataListInfo,列表类型=System.Collections.Generic.List`1[MyDataInfo][],detail=,字段名=number,字段值=102;
上述处理可以解析任何具有相同结构的未知对象,如下我添加了新的两个测试类型
using System.Collections.Generic;
public class NewDatListInfo
{
public List<NewMyDataInfo> dataList = new List<NewMyDataInfo>();
}
public class NewMyDataInfo
{
public float number = 0;
public string testName = "";
public bool flag = false;
public NewMyDataInfo(float number, string testName, bool flag)
{
this.number = number;
this.testName = testName;
this.flag = flag;
}
}
生成一个新的“未知类型对象”
public static object getDataObjNew()
{
NewDatListInfo oldData = new NewDatListInfo();
NewMyDataInfo info1 = new NewMyDataInfo(12.2f,"测试1",true);
NewMyDataInfo info2 = new NewMyDataInfo(1.2f, "测试2", false);
NewMyDataInfo info3 = new NewMyDataInfo(15.7f, "测试3", true);
oldData.dataList.Add(info1);
oldData.dataList.Add(info2); ;
oldData.dataList.Add(info3);
return oldData;
}
则运行结果将如下,能拿到NewDatListInfo的NewMyDataInfo列表,以及新的具体成员信息
根节点类型=NewDatListInfo,列表类型=System.Collections.Generic.List`1[NewMyDataInfo][],detail=,字段名=flag,字段值=True;
根节点类型=NewDatListInfo,列表类型=System.Collections.Generic.List`1[NewMyDataInfo][],detail=,字段名=flag,字段值=False;
根节点类型=NewDatListInfo,列表类型=System.Collections.Generic.List`1[NewMyDataInfo][],detail=,字段名=flag,字段值=True;
++这样的代码乍看上去似乎没什么用,但这个想法在处理解析一些不确定类型的数据对象时非常有用,尤其是他们类型不确定,但具有相同的成员结构时,如DataListInfo和NewDatListInfo虽然成员类型不同,但都是List结构且List成员里的成员又都是基本对象等等。++
反射调用成员方法
实现:获取到当现对象的类型即Type,通过Type的InvokeMember方法来间接调用对象中的指定方法
值得注意的是,这个例子测试打印数据的时候newList as List暴露了我是知道初始数据是字符串的,后面再慢慢处理初始对象未知的情况
public class Program
{
static void Main(string[] args)
{
//测试案例原数据是字符串,假定实际环境中不知道当前对象是什么类型
object unkownObj = "测试123";
Type newType = unkownObj.GetType();
object newList = createList(newType);
//反射调用List<T>中的"Add"方法,实现往数组里塞元素
object addItem = "测试新1234";
newList.GetType().InvokeMember("Add", System.Reflection.BindingFlags.Default | System.Reflection.BindingFlags.InvokeMethod, null, newList, new object[] { addItem });
//测试打印列表内容
List<string> list = newList as List<string>;
for (int index = 0; index < list.Count; index++)
{
Console.WriteLine("item=" + list[index]);
}
Console.ReadLine();
}
//创建一个指定类型的列表空内容对象
public static object createList(Type type)
{
Type listType = typeof(List<>);
Type specificType = listType.MakeGenericType(new System.Type[] { type });
return Activator.CreateInstance(specificType, new object[] { });
}
}
最后补充一个反射调函数的应用:用反射对列表排序
针对前面的 DataListInfo 类和其子结构 MyDataInfo ,并按照MyDataInfo的number字段排序,先对 DataListInfo 类添加一个指定排序的方法compareInfo():
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
public class DataListInfo
{
public List<MyDataInfo> dataList = new List<MyDataInfo>();
public int compareInfo(MyDataInfo a, MyDataInfo b)
{
return a.number - b.number;
}
}
如下为调用排序的过程,在memList.Sort(sortInfoList)之前的内容都可以说是“废代码”,因为之前已经有了,只是这里重新拿一次测试数据
using System;
using System.Collections.Generic;
using System.Reflection;
public class ReflectionSortListDemo
{
private static object dataInfo = null;
public static void testMain()
{
dataInfo = Program.getDataObj();
List<object> memList = new List<object>();
object dataList = dataInfo.getMemberValue("dataList");
int listCount = System.Convert.ToInt32(dataInfo.getMemberValue("dataList").GetType().InvokeMember("get_Count", BindingFlags.Default | BindingFlags.InvokeMethod, null, dataList, new object[] { }));
object dataItem0 = dataList.GetType().InvokeMember("get_Item", BindingFlags.Default | BindingFlags.InvokeMethod, null, dataList, new object[] { 0 });
MemberInfo[] infos = dataItem0.GetType().GetMembers();
List<string> memNameList = new List<string>();
for (int index = 0; index < infos.Length; index++)
{
if (infos[index].MemberType != MemberTypes.Field)
continue;
memNameList.Add(infos[index].Name);
}
for (int index = 0; index < listCount; index++)
{
object data = dataList.GetType().InvokeMember("get_Item", BindingFlags.Default | BindingFlags.InvokeMethod, null, dataList, new object[] { index });
memList.Add(data);
}
//排序之前
for (int index = 0; index < memList.Count; index++)
{
//打印每个成员信息
string detail = "";
for (int subIndex = 0; subIndex < memNameList.Count; subIndex++)
{
detail = ",字段:" + memNameList[subIndex] + ",值=" + memList[index].getMemberValue(memNameList[subIndex]).ToString() + ";";
}
Console.WriteLine("排序前 dataInfo.type=" + dataInfo.GetType() + ",mem[" + index + "]=" + detail);
}
//注意:包装dataInfo的排序方法并调用
memList.Sort(sortInfoList);
Console.WriteLine("\n***********************\n");
//排序之后
for (int index = 0; index < memList.Count; index++)
{
//打印每个成员信息
string detail = "";
for (int subIndex = 0; subIndex < memNameList.Count; subIndex++)
{
detail = ",字段:" + memNameList[subIndex] + ",值=" + memList[index].getMemberValue(memNameList[subIndex]).ToString() + ";";
}
Console.WriteLine("排序后 dataInfo.type=" + dataInfo.GetType() + ",mem[" + index + "]=" + detail);
}
}
private static int sortInfoList(object a, object b)
{
//注:compareInfo 是dataInfo类中的方法,我定义的是成员方法,非静态,因此target参数项需指定为dataInfo对象,表示这个对象调用自己的成员方法
return (int)dataInfo.GetType().InvokeMember("compareInfo", BindingFlags.Default | BindingFlags.InvokeMethod, null, dataInfo, new object[] { a, b });
}
}
运行结果如下:
排序前 dataInfo.type=DataListInfo,mem[0]=,字段:number,值=100;
排序前 dataInfo.type=DataListInfo,mem[1]=,字段:number,值=90;
排序前 dataInfo.type=DataListInfo,mem[2]=,字段:number,值=91;
排序前 dataInfo.type=DataListInfo,mem[3]=,字段:number,值=60;
排序前 dataInfo.type=DataListInfo,mem[4]=,字段:number,值=50;
排序后 dataInfo.type=DataListInfo,mem[0]=,字段:number,值=50;
排序后 dataInfo.type=DataListInfo,mem[1]=,字段:number,值=60;
排序后 dataInfo.type=DataListInfo,mem[2]=,字段:number,值=90;
排序后 dataInfo.type=DataListInfo,mem[3]=,字段:number,值=91;
排序后 dataInfo.type=DataListInfo,mem[4]=,字段:number,值=100;