前言
在上一章中介紹了什麼是反射:
https://www.cnblogs.com/aoximin/p/16440966.html
正文
上一節講述反射的基本原理和為什麼要用反射,還用反射的優缺點這些。
其二者的本質是一致的,都是先獲取到type(後設資料)然後在進行建立例項。
下面那個好理解看下上面那個吧。
其實還是呼叫了activator:
說另外一個故事,是先有物件,後執行構造方法。還是先執行構造方法後有物件呢?到底是編譯行為還是執行行為呢?
其實先建立物件,然後再進行建構函式。
而一切初始化其實是在建構函式中。
比如:
public class Cat
{
private string a = "100";
public string b = "100";
public Cat()
{
a = "200";
b = "200";
}
}
那麼其.ctor () 為:
計算機遠比我們想象的要簡單的多,就是開闢一塊空間,然後往裡面填充資料。 至於定義什麼型別,那屬於程式的自我管理。
有點扯遠了,那麼反射的實現也是一樣的。
在CreateInstance中:
兩者建立物件的原理基本是一致的,反射只是在上面增加了一層動態獲取型別(其中包括校驗和建立例項的程式碼生成)。
玩一個有趣的東西,既然我們說了,其實建立物件其實不一定要建構函式的。且我們上面知道了RuntimeTypeHandle 可以建立物件,那就直接搞事情。
static void Main(string[] args)
{
Type catType = typeof(Cat);
Type handleType = Type.GetType("System.RuntimeTypeHandle");
var obj = Activator.CreateInstance(handleType);
var bind = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
var hiMethod = handleType.GetMethod("Allocate", bind);
var cat= hiMethod.Invoke(obj, new object[]{catType});
Console.ReadKey();
}
得到的結果為:
好吧,接著往下看:
internal class Program
{
static void Main(string[] args)
{
var type1 = typeof(Cat);
Cat cat = new Cat();
var type2 =cat.GetType();
Assembly assembly = Assembly.GetExecutingAssembly();
var type3 = assembly.GetType("ConsoleApp1.Cat");
var type4 = typeof(Cat);
Console.WriteLine($"{type1.GetHashCode()} {type1.GetHashCode()} {type3.GetHashCode()} {type4.GetHashCode()}");
Console.ReadKey();
}
static (string name, int age, uint height) GetStudentInfo1()
{
return ("Bob", 28, 175);
}
}
他們的type也是同一個type:
internal class Program
{
static void Main(string[] args)
{
var type1 = typeof(Cat);
Cat cat = new Cat();
var type2 =cat.GetType();
Assembly assembly = Assembly.GetExecutingAssembly();
var type3 = assembly.GetType("ConsoleApp1.Cat");
var type4 = typeof(Cat);
Console.WriteLine($"{type1.GetHashCode()} {type2.GetHashCode()} {type3.GetHashCode()} {type4.GetHashCode()}");
Console.ReadKey();
}
static (string name, int age, uint height) GetStudentInfo1()
{
return ("Bob", 28, 175);
}
}
值得注意的是typeof 屬於語法糖:
現在知道了Type 就包含我們類的後設資料了,那麼這些後設資料到底有哪些呢?
裡面包含了描述類的全部資訊,有名稱空間啊,屬性啊,方法啊。這些都是有的。
這些不用去記,用的時候找找看,都有的。
唯一說一個值得的注意的地方哈。
是這樣的。有一個BindingFlags這個列舉,可以看到是是一個多選列舉。
然後這樣寫:
static void Main(string[] args)
{
var type1 = typeof(Cat);
var filter = BindingFlags.Public;
var members = type1.GetMembers(filter);
Console.ReadKey();
}
你發現看不到,這是為什麼呢?
static void Main(string[] args)
{
var type1 = typeof(Cat);
var members = type1.GetMembers();
Console.ReadKey();
}
來看下是什麼樣的。
可以看到,這個public string b,其實是public | instance 這樣的bindingflag,而不是public。
也就是說預設的是公共且可例項化的。 這個bindingflag的處理,不是或的關係,而是且的關係。
這裡也是大家使用多選列舉值得注意的地方,我們的業務上不僅可以用來做或也可以用來做且,它是多選的意思。
如果需要看下反射方法是怎麼呼叫的,可以去檢視:System.RuntimeMethodHandle的InvokeMethod,這裡面水比較深,選擇性觀看。
知道這個有什麼用呢? 因為在執行invoke的時候會經過很多判斷,如果是為了增加效能,可以直接呼叫System.RuntimeMethodHandle的InvokeMethod,一般不需要,只是說下最佳化手段。
例子
反射的常用手段,主要是一些例子。
獲取某個namespace 下面的type:
static void Main(string[] args)
{
Assembly assembly = Assembly.GetExecutingAssembly();
var types = assembly.GetTypes().Where(u => u.Namespace == "ConsoleApp1");
Console.ReadKey();
}
這個比較常用,一般來說會先載入出types,得到一個集合,然後進行管理。
有一個初學者容易犯的錯誤,就是認為:
認為這種是屬性,其實這個英文是property,是特性的意思,這在有些中文書上直接說成屬性,這是錯誤的。
然後:
這種是attribute。
上面這種是欄位,叫做field。
然後全部的這些,叫做member,都是成員:
這些概念是要區分開的。
第三個小栗子,如果獲取靜態的值:
static void Main(string[] args)
{
Type type = typeof(Cat);
var binding = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static;
var a= type.GetProperties(binding);
Cat.Test = "123456";
foreach (var item in a)
{
var test= item.GetValue(null);
Console.WriteLine($"test:{test}");
}
Console.ReadKey();
}
上面要說明的是GetValue,對於靜態可以填空。
我們講述到前面的建構函式,都是預設的建構函式。
static void Main(string[] args)
{
Type type = typeof(Cat);
var cat = Activator.CreateInstance(type);
Console.ReadKey();
}
這在一般情況下,很難適應,一般我們的建構函式在寫一些複雜一點的時候,都會傳入引數的。
public Cat(string a, string b)
{
this.a = a;
this.b = b;
}
這樣的。那麼怎麼例項化呢?
static void Main(string[] args)
{
Type type = typeof(Cat);
var constructorInfo = type.GetConstructor(new Type[]{ typeof(string), typeof(string) });
var cat =constructorInfo.Invoke(new object[] { "123", "456"});
Console.ReadKey();
}
其他的方法也是這樣。
結
上一節講述了反射,這一節講了一下反射的大致的行為和一些簡單的例子。下一節可能是io流相關的,以前寫過Java的,其實兩者都一樣,考慮是否重新整合一下。或者直接開篇非同步篇。