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

語言: CN / TW / HK

一、場景假設

假設現在有一個泛型類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--------------

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