一、反射
什麼是反射
.Net的應用程式由幾個部分:‘程式集(Assembly)’、‘子產品(Module)’、‘類型(class)’組成;
反射提供一種程式設計的方式,讓程式員可以在程式運作期獲得這幾個組成部分的相關資訊;
Type類可以獲得對象的類型資訊:方法、構造器、屬性、字段;
這些都包含在System.Reflection命名空間下;
程式集(assembly)
.dll編譯好的庫,裡面可能有多個命名空間;.exe可執行檔案;這兩個種都是程式集;
子產品(Module)
C#中沒有子產品,但提供了類似的功能:靜态變量;
在C#中子產品的另外一個含義是:
一種可加載單元,它可以包含類型聲明和類型實作。子產品包含的資訊足以使公共語言運作庫在子產品加載時找到所有的實作位。子產品的格式是 Windows 可移植可執行 (PE) 檔案格式的擴充。在部署時,子產品總是包含在程式集中;
個人覺得C#中沒有太注重子產品,有另一個概念,命名空間;将多個一類功能的類組合在同一命名空間下;
作用
運作期得到類型資訊,修改屬性或調用方法;
提高可擴充性;
二、反射方法
測試類,包含有參無參數構造;屬性,方法,字段;
//測試類
class Person
{
//構造
public Person(){}
public Person(string name)
{
this.name = name;
}
//字段
string name;
int age;
//屬性
public string Name{
get{
return name;
}
set{
name = value;
}
}
//方法
public void SetName(string input)
{
name = input;
}
}
擷取字段
通過Type類可獲得以下資訊;
Person p = new Person
Type t = typeof(p)
//擷取所有字段
FieldInfo[] fieldInfos = type.GetFields();
//擷取單個字段
FieldInfo fieldInfo = type.GetFields("name");
fieldInfo.SetValue(p,"perilla")
擷取屬性
Person p = new Person
Type t = typeof(p)
//擷取所有屬性
PropertyInfo[] propertyInfos = type.GetProperties();
//擷取單個屬性
PropertyInfo propertyInfo = type.GetProperty("Name");
propertyInfo.SetValue(p,"perilla")
擷取方法
Person p = new Person
Type t = typeof(p)
//擷取所有屬性
MethodInfo[] methodInfos = type.GetMethods();
//擷取單個屬性
MethodInfo methodInfo = type.GetMethod("SetName");
methodInfo.Invoke(p,"Perilla")
動态建立
Person p = new Person
Type t = typeof(p)
object obj = System.Activator.CreateInstance(t,"Perilla")
//也可以擷取構造通過構造建立
三、反射工具類
public class ReflectTool
{
#region 成員讀寫
/// <summary>
/// 通過參數清單,填充實體類型
/// </summary>
/// <param name="model">實體對象</param>
/// <param name="objs">資料隻讀器</param>
public static void FillInstanceValue(object model, params object[] objs)
{
Type type = model.GetType();
PropertyInfo[] properties = type.GetProperties();
for (int i = 0; i < objs.Length; i++)
{
properties[i].SetValue(model, objs[i], null);
}
}
/// <summary>
/// 通過kv參數表,填充實體類型
/// </summary>
/// <param name="model">實體對象</param>
/// <param name="objs">資料隻讀器</param>
public static void FillInstanceValue(object model, Dictionary<string,object> paramDic)
{
Type type = model.GetType();
PropertyInfo[] properties = type.GetProperties();
for (int i = 0; i < paramDic.Count; i++)
{
if (paramDic.ContainsKey(properties[i].Name))
{
properties[i].SetValue(model, paramDic[properties[i].Name]);
}
}
}
public static void FillInstanceField(object model, Dictionary<string, object> paramDic)
{
Type type = model.GetType();
FieldInfo[] fieldInfos = type.GetFields();
for (int i = 0; i < paramDic.Count; i++)
{
if (paramDic.ContainsKey(fieldInfos[i].Name))
{
Type it = fieldInfos[i].GetType();
fieldInfos[i].SetValue(model, paramDic[fieldInfos[i].Name]);
}
}
}
/// <summary>
/// 擷取實體相關屬性的值
/// </summary>
/// <param name="obj"></param>
/// <param name="propertyName"></param>
/// <returns></returns>
public static object GetInstanceValue(object obj, string propertyName)
{
object objRet = null;
if (!string.IsNullOrEmpty(propertyName))
{
PropertyDescriptor descriptor = TypeDescriptor.GetProperties(obj).Find(propertyName, true);
if (descriptor != null)
{
objRet = descriptor.GetValue(obj);
}
}
return objRet;
}
#endregion
#region 方法調用
/// 直接調用内部對象的方法/函數或擷取屬性(支援重載調用)
/// <param name="refType">目标資料類型</param>
/// <param name="funName">函數名稱,區分大小寫。</param>
/// <param name="objInitial">如果調用屬性,則為相關對象的初始化資料(帶參數構造,隻能調用Get),否則為Null。</param>
/// <param name="funParams">函數參數資訊</param>
public static object InvokeMethodOrGetProperty(Type refType, string funName, object[] objInitial, params object[] funParams)
{
MemberInfo[] mis = refType.GetMember(funName);
if (mis.Length < 1)
{
throw new InvalidProgramException(string.Concat("函數/方法 [", funName, "] 在指定類型(", refType.ToString(), ")中不存在!"));
}
else
{
MethodInfo targetMethod = null;
StringBuilder pb = new StringBuilder();
foreach (MemberInfo mi in mis)
{
if (mi.MemberType != MemberTypes.Method)
{
if (mi.MemberType == MemberTypes.Property)
{
#region 調用屬性方法Get
targetMethod = ((PropertyInfo)mi).GetGetMethod();
break;
#endregion
}
else
{
throw new InvalidProgramException(string.Concat("[", funName, "] 不是有效的函數/屬性方法!"));
}
}
else
{
#region 檢查函數參數和資料類型 綁定正确的函數到目标調用
bool validParamsLen = false, validParamsType = false;
MethodInfo curMethod = (MethodInfo)mi;
ParameterInfo[] pis = curMethod.GetParameters();
if (pis.Length == funParams.Length)
{
validParamsLen = true;
pb = new StringBuilder();
bool paramFlag = true;
int paramIdx = 0;
#region 檢查資料類型 設定validParamsType是否有效
foreach (ParameterInfo pi in pis)
{
pb.AppendFormat("Parameter {0}: Type={1}, Name={2}\n", paramIdx, pi.ParameterType, pi.Name);
//不對Null和接受Object類型的參數檢查
if (funParams[paramIdx] != null && pi.ParameterType != typeof(object) &&
(pi.ParameterType != funParams[paramIdx].GetType()))
{
#region 檢查類型是否相容
try
{
//TODO====這裡有問題,執行個體是子類對象,參數是父類,父父類接口,無法轉化,但是确實是參數;
funParams[paramIdx] = Convert.ChangeType(funParams[paramIdx], pi.ParameterType);
}
catch (Exception)
{
paramFlag = false;
}
#endregion
//break;
}
++paramIdx;
}
#endregion
if (paramFlag == true)
{
validParamsType = true;
}
else
{
continue;
}
if (validParamsLen && validParamsType)
{
targetMethod = curMethod;
break;
}
}
#endregion
}
}
if (targetMethod != null)
{
object objReturn = null;
#region 兼顧效率和相容重載函數調用
try
{
object objInstance = System.Activator.CreateInstance(refType, objInitial);
objReturn = targetMethod.Invoke(objInstance, BindingFlags.InvokeMethod, Type.DefaultBinder, funParams,
System.Globalization.CultureInfo.InvariantCulture);
}
catch (Exception)
{
objReturn = refType.InvokeMember(funName, BindingFlags.InvokeMethod, Type.DefaultBinder, null, funParams);
}
#endregion
return objReturn;
}
else
{
throw new InvalidProgramException(string.Concat("函數/方法 [", refType.ToString(), ".", funName,
"(args ...) ] 參數長度和資料類型不正确!\n 引用參數資訊參考:\n",
pb.ToString()));
}
}
}
/// 調用相關實體類型的函數方法
/// <param name="refType">實體類型</param>
/// <param name="funName">函數名稱</param>
/// <param name="funParams">函數參數清單</param>
public static object InvokeFunction(Type refType, string funName, params object[] funParams)
{
return InvokeMethodOrGetProperty(refType, funName, null, funParams);
}
#endregion
#region 建立執行個體
//通過Type建立執行個體,傳回Object
public static object CreateInstance(Type refType, params object[] objInitial)
{
object res = System.Activator.CreateInstance(refType, objInitial);
return res;
}
//通過泛型T建立執行個體,并傳回T
public static T CreateInstance<T>(params object[] objInitial) where T : new()
{
Type refType = typeof(T);
object res = System.Activator.CreateInstance(refType, objInitial);
if (res == null)
{
Debug.LogError("Reflect create T is null");
}
return (T)res;
}
#endregion
}
調用測試
public class Test
{
public Test() { }
public Test(string name) { this.name = name; }
private string name;
public string Name {
get {
//Debug.Log($"PorperityGet:{name}");
return name;
}
set {
name = value;
//Debug.Log($"PorperitySet:{name}");
}
}
public void Func1()
{
Debug.Log("Test.Func1");
}
public void Func2(int para1)
{
Debug.Log($"Test.Func1:{para1}");
}
public void Func3(string para1,int para2)
{
Debug.Log($"Test.Func1:{para1},{para2}");
}
}
void Start()
{
Test obj = new Test();
Type t = obj.GetType();
ReflectTool.InvokeFunction(t, "Func1");
ReflectTool.InvokeFunction(t, "Func2", 5);
ReflectTool.InvokeFunction(t, "Func3", "perilla", 25);
object[] objs = new object[1];
objs[0] = "perilla";
ReflectTool.InvokeMethodOrGetProperty(t, "Name", objs);
ReflectTool.InvokeFunction(t, "Name");
obj.Name = "hhahah";
ReflectTool.FillInstanceValue(obj, "QAQ");
Dictionary<string, object> dic = new Dictionary<string, object>();
dic["Name"] = "QWQ";
ReflectTool.FillInstanceValue(obj, dic);
Debug.Log(ReflectTool.GetInstanceValue(obj, "Name"));
obj = ReflectTool.CreateInstance(t, "skylar") as Test;
Debug.Log(obj.Name);
obj = ReflectTool.CreateInstance<Test>("Rebort");
Debug.Log(obj.Name);
string str = " adfas dfaf ffff dsawee dff ";
str = System.Text.RegularExpressions.Regex.Replace(str, @"\s", "");
Debug.Log(str);
Config config = ConfigTool.GetConfig<Config>(Application.dataPath+"/Config.cfg");
Debug.Log($"ServerAddr:{config.ServerIp};Port:{config.Port}");
}

四、特性
待完善...
Life is too short for so much sorrow.