C# 隨機給一個全部信息都未知的類類型,如何獲取該類的類名、屬性個數、屬性名、屬性的數據類型、屬性值?

一、場景假設

假設現在有一個泛型類T的實例對象t,該T類的全部信息都未知。

要求:打印輸出實例對象t的類名、屬性個數、屬性名、屬性的數據類型、屬性值。

二、解決問題

1、我們根據輸出的內容要求定義一個實體類如下:

    public class GeneralDataModel
    {
        /// <summary>
        /// 類名
        /// </summary>
        public string class_name { get; set; }

        /// <summary>
        /// 屬性個數
        /// </summary>
        public int prop_count { get; set; }

        /// <summary>
        /// 單個屬性的信息
        /// </summary>
        public List<PropInfoItem> props { get; set; }
    }
    public class PropInfoItem
    {
        /// <summary>
        /// 屬性名
        /// </summary>
        public string prop_name { get; set; }

        /// <summary>
        /// 屬性數據類型
        /// </summary>
        public string prop_data_type { get; set; }

        /// <summary>
        /// 屬性值
        /// </summary>
        public string prop_value { get; set; }
    }

2、編寫一個方法,該方法的主要功能是解析實例對象t,並輸出步驟1中格式的內容。方法代碼實現如下:

        public static GeneralDataModel DataAnalysis<T>(T t)
        {
            var data_type = t.GetType();
            var propInfo = data_type.GetProperties();
            var list = new List<PropInfoItem>();
            foreach (var item in propInfo)
            {
                var e = new PropInfoItem
                {
                    prop_name = item.Name,
                    prop_data_type = item.PropertyType.Name,
                    prop_value = item.GetValue(t) == null ? "" : item.GetValue(t).ToString()
                };
                list.Add(e);
            }

            var res = new GeneralDataModel
            {
                class_name = data_type.Name,
                prop_count = propInfo.Count(),
                props = list
            };

            return res;
        }

三、驗證方法功能

1、假設現在有一個學生類如下所示:

    public class Student
    {
        /// <summary>
        /// 學號
        /// </summary>
        public int no { get; set; }

        /// <summary>
        /// 姓名
        /// </summary>
        public string name { get; set; }

        /// <summary>
        /// 年級
        /// </summary>
        public string grade { get; set; }

        /// <summary>
        /// 出生年月
        /// </summary>
        public DateTime birth { get; set; }
    }

2、根據該類實例化了一個st對象如下:

   var st = new Student()
   {
       no = 123456,
       name = "張三",
       grade = "六年級",
       birth = DateTime.Now
    };

3、調用DataAnalysis方法解析st,並打印輸出結果:

   var res = DataAnalysis(st); 
   Console.WriteLine(JsonConvert.SerializeObject(res));

4、輸出結果如下:

{
    "class_name": "Student",
    "prop_count": 4,
    "props": [
        {
            "prop_name": "no",
            "prop_data_type": "Int32",
            "prop_value": "123456"
        },
        {
            "prop_name": "name",
            "prop_data_type": "String",
            "prop_value": "張三"
        },
        {
            "prop_name": "grade",
            "prop_data_type": "String",
            "prop_value": "六年級"
        },
        {
            "prop_name": "birth",
            "prop_data_type": "DateTime",
            "prop_value": "2022/5/7 17:21:12"
        }
    ]
}

5、看到輸出結果後,感覺完美的解決了問題。

四、變化無常

1、因為種種原因,學生類增加了兩個屬性,同時實例化對象的創建形式也變了,變化後的形式如下:

    public class Student
    {
        public Student()
        {

        }

        public Student(string id_card_no, string address)
        {
            this.id_card_no = id_card_no;
            this.address = address;
        }


        /// <summary>
        /// 學號
        /// </summary>
        public int no { get; set; }

        /// <summary>
        /// 姓名
        /// </summary>
        public string name { get; set; }

        /// <summary>
        /// 年級
        /// </summary>
        public string grade { get; set; }

        /// <summary>
        /// 出生年月
        /// </summary>
        public DateTime birth { get; set; }

        /// <summary>
        /// 身份證(受保護類型)
        /// </summary>
        protected string id_card_no { get; set; }

        /// <summary>
        /// 家庭地址(私有類型)
        /// </summary>
        private string address { get; set; }
    }
   var st = new Student("777888202005071111", "家庭地址私有,暫時不方便透露")
   {
       no = 123456,
       name = "張三",
       grade = "六年級",
       birth = DateTime.Now
   };

2、再次調用DataAnalysis方法解析st,並打印輸出結果:

   var res = DataAnalysis(st);
   Console.WriteLine(JsonConvert.SerializeObject(res));

3、輸出結果如下:

{
    "class_name": "Student",
    "prop_count": 4,
    "props": [
        {
            "prop_name": "no",
            "prop_data_type": "Int32",
            "prop_value": "123456"
        },
        {
            "prop_name": "name",
            "prop_data_type": "String",
            "prop_value": "張三"
        },
        {
            "prop_name": "grade",
            "prop_data_type": "String",
            "prop_value": "六年級"
        },
        {
            "prop_name": "birth",
            "prop_data_type": "DateTime",
            "prop_value": "2022/5/7 17:40:21"
        }
    ]
}

4、看到輸出結果時,咦?怎麼似乎好像哪裡不對?新增的兩個屬性怎麼沒有被解析並輸出呢?

五、反射了解一下?

1、通過種種途徑或者查閱其他資料你了解到了反射的相關知識,並找到了一個名為GetRuntimeProperties的方法。

2、修改原先的解析方法代碼如下:

    public static GeneralDataModel DataAnalysis<T>(T t)
    {
        var data_type = t.GetType();
        var refPropInfo = data_type.GetRuntimeProperties();
        var list = new List<PropInfoItem>();
        foreach (var item in refPropInfo)
        {
            var e = new PropInfoItem
            {
                prop_name = item.Name,
                prop_data_type = item.PropertyType.Name,
                prop_value = item.GetValue(t) == null ? "" : item.GetValue(t).ToString()
            };
            list.Add(e);
        }

        var res = new GeneralDataModel
        {
            class_name = data_type.Name,
            prop_count = refPropInfo.Count(),
            props = list
        };

        return res;
    }

3、再一次調用DataAnalysis方法解析st,並打印輸出結果:

   var res = DataAnalysis(st);
   Console.WriteLine(JsonConvert.SerializeObject(res));

 4、輸出結果如下:

{
    "class_name": "Student",
    "prop_count": 6,
    "props": [
        {
            "prop_name": "no",
            "prop_data_type": "Int32",
            "prop_value": "123456"
        },
        {
            "prop_name": "name",
            "prop_data_type": "String",
            "prop_value": "張三"
        },
        {
            "prop_name": "grade",
            "prop_data_type": "String",
            "prop_value": "六年級"
        },
        {
            "prop_name": "birth",
            "prop_data_type": "DateTime",
            "prop_value": "2022/5/7 17:52:12"
        },
        {
            "prop_name": "id_card_no",
            "prop_data_type": "String",
            "prop_value": "777888202005071111"
        },
        {
            "prop_name": "address",
            "prop_data_type": "String",
            "prop_value": "家庭地址暫時不方便透露"
        }
    ]
}

5、看到這輸出結果,臉上露出了滿意的笑容,啊~~~問題終於解決了,開森^_^

六、前後對比並溯源

1、方法前後變化僅僅只有一處,由GetProperties變為了GetRuntimeProperties

2、溯源發現:

  • GetProperties:在System命名空間下,是Type類的實例方法。
  • GetRuntimeProperties(Type類的擴展方法):在System.Reflection命名空間下,是RuntimeReflectionExtensions類的靜態方法。

————–The  End————–

———-本篇文章到此結束———-