C#反射解析資料內容,呼叫方法
反射獲取成員值
這部分主要是為了拿到例項的時候我們可能不清楚例項或成員的型別,但知道起成員的名稱或需要拿到其全部成員資訊,這個時候就值得用到反射來獲取類的詳細資訊了。
下面顯示用反射來獲取一個物件某個成員的值。
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;