十年河東,十年河西,莫欺少年窮
學無止境,精益求精
反射反射,程序員的快樂,利用反射可以獲取到私有屬性及其值
在C#中反射無處不在,用好反射,就可以爲所欲爲
有這麼一個學生類:
public class student { /// <summary> /// 年齡 /// </summary> public int stuAge; /// <summary> /// 是否是管理者 /// </summary> private bool isManager; /// <summary> /// 學號 /// </summary> private string stuNo { get; set; } /// <summary> /// 性名 /// </summary> public string stuName { get; set; } /// <summary> /// 性別 /// </summary> public string stuSex { get; set; } /// <summary> /// 班級 /// </summary> private string grand { get; set; } public student() { } public student(string stuNo,string grand) { this.stuNo = stuNo; this.grand = grand; } public string Getsss() { return "ssss"; } public string Getsss(string sss) { return sss; } }
學號、班級爲私有屬性。姓名、性別公有屬性。年齡,是否是管理者爲成員變量。並擁有無參和有參兩種構造函數和兩個自定義方法,那麼,如何利用反射對它進行操作呢,且聽我慢慢道來
1、遍歷屬性
1.1、遍歷私有屬性
static void Main(string[] args) { var stu = new student("001","魔教"); stu.stuName = "東方不敗"; stu.stuSex = "人妖"; var typ2 = stu.GetType(); /// Console.WriteLine("---------------------獲取私有屬性------------------------"); foreach (var item in typ2.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic)) { Console.WriteLine(item.Name + ":" + item.PropertyType + ":" + item.CanRead); } }
1.2、遍歷公有有屬性
Console.WriteLine("---------------------獲取公有屬性------------------------"); foreach (var item in typ2.GetProperties(BindingFlags.Instance | BindingFlags.Public)) { Console.WriteLine(item.Name + ":" + item.PropertyType + ":" + item.CanRead); }
1.3、遍歷所有屬性
Console.WriteLine("---------------------獲取所有屬性------------------------"); foreach (var item in typ2.GetProperties(BindingFlags.Instance | BindingFlags.Public|BindingFlags.NonPublic)) { Console.WriteLine(item.Name + ":" + item.PropertyType + ":" + item.CanRead); }
1.4、獲取某個屬性
var p_1 = typ2.GetProperty("stuSex"); Console.WriteLine(p_1?.Name);
2、遍歷字段
遍歷字段和遍歷屬性類似,不同的是,遍歷字段可以將類中的成員變量和屬性一塊遍歷出來
2.1、遍歷私有字段
static void Main(string[] args) { var stu = new student("001","魔教"); stu.stuName = "東方不敗"; stu.stuSex = "人妖"; var typ2 = stu.GetType(); Console.WriteLine("---------------------獲取私有字段------------------------"); // var flds = typ2.GetFields(BindingFlags.Instance | BindingFlags.NonPublic); foreach (var item in flds) { Console.WriteLine(item.Name + ":" + item.FieldType + ":" + item.IsStatic); } }
遍歷私有字段時,會遍歷所有私有屬性及成員變量,但stuName 和 stuSex 兩個公有的屬性爲什麼也被遍歷出來了?
問題出在get;set;構造器,在原始的C#中,構造器是這樣的
private int money;//私有字段 public int Money //屬性 { //GET訪問器,可以理解成另類的方法,返回已經被賦了值的私有變量money get { return money; } //SET訪問器,將我們打入的值賦給私有變量money set { money = value; } }
現在的get;set;構造器只是一種簡寫,因此在C#底層,依舊會採用上述構造。這樣公有的屬性被遍歷私有字段時帶出來就不足爲奇了!
2.2、遍歷公有字段
Console.WriteLine("---------------------獲取公有字段------------------------"); // var flds = typ2.GetFields(BindingFlags.Instance | BindingFlags.Public); foreach (var item in flds) { Console.WriteLine(item.Name + ":" + item.FieldType + ":" + item.IsStatic); }
2.3、遍歷所有字段
Console.WriteLine("---------------------獲取所有字段------------------------"); // var flds = typ2.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (var item in flds) { Console.WriteLine(item.Name + ":" + item.FieldType + ":" + item.IsStatic); }
2.4、獲取某個字段
var stu = new student("001","魔教"); stu.stuName = "東方不敗"; stu.stuSex = "人妖"; var typ2 = stu.GetType(); Console.WriteLine("---------------------獲取某個字段------------------------"); // var fld = typ2.GetField("stuAge"); Console.WriteLine(fld?.Name);
3、獲取其他常用信息
static void Main(string[] args) { var stu = new student("001","魔教"); stu.stuName = "東方不敗"; stu.stuSex = "人妖"; var typ2 = stu.GetType(); Console.WriteLine("--------------------獲取其他信息-------------------------"); Console.WriteLine("他的全稱:" + typ2.FullName); Console.WriteLine("他的命名空間:" + typ2.Namespace); Console.WriteLine("他的名稱:" + typ2.Name); Console.WriteLine("他的基類:" + typ2.BaseType); }
4、通過反射創建對象
4.1、利用無參構造函數創建對象
Activator.CreateInstance(obj)
執行無參構造函數
static void Main(string[] args) { Console.WriteLine("--------------------通過反射創建對象-------------------------"); var stu_RF = typeof(student); ///執行無參構造函數 var stu = Activator.CreateInstance(stu_RF) as student; stu.stuName = "東方不敗"; stu.stuSex = "人妖"; stu.stuAge = 18; Console.WriteLine($"姓名:{stu.stuName},性別:{stu.stuSex},年齡:{stu.stuAge}"); }
這裏創建的對象相當於
student s = new student() { stuAge = 18, stuName = "東方不敗", stuSex = "人妖" };
4.2、利用有參構造函數創建對象
Activator.CreateInstance(obj,params object[] args)
執行有參構造函數,args 爲參數
static void Main(string[] args) { Console.WriteLine("--------------------通過反射創建對象-------------------------"); var stu_RF = typeof(student); ///執行有參構造函數 var stu = Activator.CreateInstance(stu_RF,"001","魔教") as student; stu.stuName = "東方不敗"; stu.stuSex = "人妖"; stu.stuAge = 18; Console.WriteLine($"姓名:{stu.stuName},性別:{stu.stuSex},年齡:{stu.stuAge}"); }
這裏創建的對象相當於
student s = new student("001","魔教") { stuAge = 18, stuName = "東方不敗", stuSex = "人妖" };
4.3、利用泛型方法創建對象
Activator.CreateInstance<T>
利用泛型構造對象時,只能執行無參構造函數
static void Main(string[] args) { Console.WriteLine("--------------------通過反射創建對象-------------------------"); var stu_RF = typeof(student); ///執行無參構造函數 var stu = Activator.CreateInstance<student>(); stu.stuName = "東方不敗"; stu.stuSex = "人妖"; stu.stuAge = 18; Console.WriteLine($"姓名:{stu.stuName},性別:{stu.stuSex},年齡:{stu.stuAge}"); }
5、設置或讀取反射創建的對象的屬性/字段
通過反射創建對象後,我們可以通過setValue 和 getValue 來設置/讀取相關屬性/字段的值
static void Main(string[] args) { var stu_RF = typeof(student); ///執行有參構造函數 var stu = Activator.CreateInstance(stu_RF, "001", "魔教") as student; var typ2 = stu.GetType(); var p_1 = typ2.GetProperty("stuName"); p_1.SetValue(stu, "東方不敗"); var p_2 = typ2.GetProperty("stuSex"); p_2.SetValue(stu, "人妖"); //獲取私有屬性 需要聲明 BindingFlags.Instance|BindingFlags.NonPublic var p_3 = typ2.GetProperty("grand",BindingFlags.Instance|BindingFlags.NonPublic); p_3.SetValue(stu, "魔教"); //獲取公有字段 用GetField 需要聲明 BindingFlags.Instance|BindingFlags.Public var p_4 = typ2.GetField("stuAge", BindingFlags.Instance | BindingFlags.Public); p_4.SetValue(stu, 18); //獲取私有字段 用GetField 需要聲明 BindingFlags.Instance|BindingFlags.NonPublic var p_5 = typ2.GetField("isManager", BindingFlags.Instance | BindingFlags.NonPublic); p_5.SetValue(stu, true); // bool 值需要特殊處理下 var zhiweibol = (bool)p_5?.GetValue(stu); string zhiwei = string.Empty; if (zhiweibol) { zhiwei = "教主"; } Console.WriteLine($"姓名爲:{p_1?.GetValue(stu)},性別爲:{p_2?.GetValue(stu)},所屬教派爲:{p_3?.GetValue(stu)},年齡爲:{p_4?.GetValue(stu)},職位爲:{zhiwei}"); Console.ReadKey(); }
6、通過反射,加載程序集
6.1、通過dll文件路徑加載程序集
新建一個類庫項目,命名爲:swapModels,控制檯程序引用該項目
static void Main(string[] args) { //讀取當前Program類所在路徑 var rootPath = Path.GetDirectoryName(typeof(Program).Assembly.Location); var rootDir = new DirectoryInfo(rootPath); var basePath = rootDir.FullName; var dllName = "swapModels.dll"; string assemblyPath = Path.Combine(basePath, dllName); //通過路徑加載程序集 var assembly = Assembly.LoadFrom(assemblyPath); Console.ReadKey(); }
6.2、通過命名空間加載程序集
新建一個類庫項目,命名爲:swapModels,控制檯程序引用該項目
static void Main(string[] args) { var assembly = Assembly.LoadFrom("swapModels"); Console.ReadKey(); }
以上兩種方法都可以加載引用項目所在的程序集。
7、通過反射操作構造方法
7.1、無參構造方法
static void Main(string[] args) { var typ = typeof(student); //獲取無參構造函數 var Constructor = typ.GetConstructor(new Type[] { }); //無參構造函數創建的對象 var obj = Constructor.Invoke(null) as student; // var obj2 = Activator.CreateInstance(typ) as student; if (obj.Equals(obj2)) { Console.WriteLine("兩個無參對象一致"); } else { Console.WriteLine("兩個無參對象不一致"); } }
7.2、有參構造方法
static void Main(string[] args) { var typ = typeof(student); //獲取有參構造函數--註明構造方法的參數類型 var ConstructorYc = typ.GetConstructor(new Type[] { typeof(string),typeof(string)}); //傳入參數 var obj3 = ConstructorYc.Invoke(new object[] { "001", "魔教" } ) as student; obj3.stuName = "東方不敗"; Console.ReadKey(); }
8、通過反射,操作方法【其他普通方法】
8.1、獲取所有方法
static void Main(string[] args) { var typ = typeof(student); //獲取有參構造函數--註明構造方法的參數類型 var lst = typ.GetMethods( BindingFlags.Instance|BindingFlags.Public| BindingFlags.NonPublic); foreach(var item in lst) { Console.WriteLine(item.Name + ":" + item.ReturnType); } Console.ReadKey(); }
8.2、調用方法
Getsss 有兩個方法重載,因此,在調用自定義方法時,需要指定有無參數,如果有參數,需要指定參數類型及傳入響應參數
static void Main(string[] args) { var typ = typeof(student); //調用無參方法 var Mth = typ.GetMethod("Getsss",new Type[] { }); var ConstructorYc = typ.GetConstructor(new Type[] { typeof(string), typeof(string) }); //傳入參數 var obj = ConstructorYc.Invoke(new object[] { "001", "魔教" }) as student; var result_1 = Mth.Invoke(obj ,null); Console.WriteLine(result_1); //調用有參數的方法 指定參數類型 var Mth2 = typ.GetMethod("Getsss", new Type[] {typeof(string) }); //傳入參數 var result_2 = Mth2.Invoke(obj,new object[] { "哈哈哈哈"}); Console.WriteLine(result_2); Console.ReadKey(); }
@天才臥龍的博客