1. 程式人生 > 其它 >C# Reflection反射機制

C# Reflection反射機制

一、反射

什麼是反射

.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 方法呼叫
    /// <summary>
    /// 直接呼叫內部物件的方法/函式或獲取屬性(支援過載呼叫)
    /// </summary>
    /// <param name="refType">目標資料型別</param>
    /// <param name="funName">函式名稱,區分大小寫。</param>
    /// <param name="objInitial">如果呼叫屬性,則為相關物件的初始化資料(帶引數構造,只能呼叫Get),否則為Null。</param>
    /// <param name="funParams">函式引數資訊</param>
    /// <returns>執行結果</returns>
    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
                                {
                                    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()));
            }
        }

    }

    /// <summary>
    /// 呼叫相關實體型別的函式方法
    /// </summary>
    /// <param name="refType">實體型別</param>
    /// <param name="funName">函式名稱</param>
    /// <param name="funParams">函式引數列表</param>
    /// <returns>呼叫該函式之後的結果</returns>
    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.