反射和激活類型
獲取類型
1.GetType()和typeof都可以獲取類型,GetType是在運行時,而typeof是在編譯時
2.可以在獲取一個基礎類型後再構造其他類型
Type simpleArray = typeof(int).MakeArrayType();
Console.WriteLine(simpleArray == typeof(int[]));
先獲取int,再獲取int類型的數組和直接獲取int[]類型是一樣的。
3.GetElementType可以返回數組元素的類型
4.獲取嵌套類型GetNestedTypes()
5.類型具有Namespace,Name,Fullname。一般Fullname=Name+Namespace
6.泛型類型帶有'後綴,數字表示有幾個參數
7.Type具有BaseType屬性,可以查找基類
8.GetInterfaces方法會返回類型實現的接口
9.和is等價的動態運算符 IsInstanceOfType,IsAssignableFrom
10.從類型創建實例:
int i = (int)Activator.CreateInstance(typeof(int));
ConstructorInfo ci = typeof(X).GetConstructor(new[] { typeof(string) });
object foo = ci.Invoke(new object[] { null });
11.Type可以表示泛型類型,也可以表示封閉類型,MakeGenericType可以將泛型轉爲封閉類型。GetGenericTypeDefinition可以實現翻轉。
反射並調用成員
1.GetMembers可以返回類型的成員
2.每一個屬性、字段和方法的類型的MemberInfo
3.GetMembers會返回所有類型的成員,可以傳參MemberTypes枚舉來限定返回的成員類型
4.還有專門的返回固定類型的方法,GetMethods,GetFields。。。
成員類型
1.MemberInfo是所有類型信息的抽象基類
2.不同的類型可以由不同的MemberInfo對應的子類去接受
3.*Info實例都會在第一次使用時由反射API緩存,增加性能
4.一個屬性有get和set方法,可以在獲取屬性後獲取它的get和set方法
5.獲取屬性後就可以實現動態綁定了
object s = "Hello";
PropertyInfo prop = s.GetType().GetProperty("Length");
int length = (int)prop.GetValue(s, null);
6.方法的調用
Type type = typeof(string);
Type[] par = { typeof(int),typeof(int) };
MethodInfo method = type.GetMethod("Substring", par);
object[] arguement = { 2,1 };
object returnValue = method.Invoke("stamp", arguement);
Console.WriteLine(returnValue);
對於方法重載,要注意參數的設置
7.檢索調用泛型方法,需要遍歷所有泛型方法,找到符合的那個方法再調用。用表達式樹則可以省略很多
8.動態調用爲幾微秒,用實例委託來代替可以降至幾納秒。
MethodInfo m = typeof(string).GetMethod("Trim", new Type[0]);
var trim = (StringToString)Delegate.CreateDelegate(typeof(StringToString), m);
原理就是讓反射只發生一次
9.BindingFlags可以改變枚舉,返回非公有成員
10.使用泛型方法時先要給泛型確定類型
MethodInfo m = typeof(Program).GetMethod("Echo");
MethodInfo m2 = m.MakeGenericMethod(typeof(int));
Console.WriteLine(m2.Invoke(null,new object[] { 3}));
11.調用未知類型的泛型接口成員
通過多次判斷類型(用is),來對不同的類型做出不同的特殊處理
反射程序集
1.返回命名空間下的類型
Type t = Assembly.GetExecutingAssembly().GetType("Net.Program");
2.從類型獲得程序集
var a = typeof(Test).GetType().Assembly.GetType();
3.加載一個程序集
Assembly a = Assembly.LoadFrom(@"E:\obj\Debug\Framework.exe");
4.由於可以獲得一個程序集的所有類型,所以可以將一些類型專門構成一個程序集。像Asp.Net Core中的Service和其實現類就可以用這種方法
使用特性
CLR允許使用特性將額外的元數據追加到類型、成員和程序集上
特性基礎
1.C#三類特性:位映射特性、自定義特性、僞自定義特性
2.位映射特性:public、abstract。。。
3.自定義特性:自己定義的特性
4.僞自定義特性:系統帶的特性,會由內部轉爲位映射特性
AttributeUsage特性
1.AttributeUsage可以定義特性的特性。
2.AllowMultiple:能否在目標上運用多次
3.Inherited:能否被子類繼承
4.ValidOn:目標集合
定義自定義特性
public class Person
{
[Custom1(Num =4,Name ="hi")]
public string Name { get; set; }
private int Age;
}
[AttributeUsage(AttributeTargets.Property)]
public class Custom1Attribute : Attribute
{
public int Num { get; set; }
public string Name { get; set; }
}
AttributeUsage限定屬性,特性可以給屬性傳參,也可以用構造函數來傳參
在運行時檢索特性
自定義特性用CustomAttribute,GetCustomAttribute來獲取
動態生成代碼
1.DynamicMethod可以在運行時生成方法
2.OpCodes對每一個IL操作碼都有一個對應的靜態只讀字段
MethodInfo c = typeof(Program).GetMethod("HelloWorld", BindingFlags.Static | BindingFlags.NonPublic);
b.Emit(OpCodes.Call,c);
b.Emit(OpCodes.Ret);
a.Invoke(null, null);
3.評估棧就是參數的臨時存放區
4.將整數加載至評估棧
MethodInfo c = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int)});
b.Emit(OpCodes.Ldc_I4,123);
b.Emit(OpCodes.Call, c);
b.Emit(OpCodes.Ret);
5.Add將一個數字彈出,計算後在壓入
MethodInfo c = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int)});
b.Emit(OpCodes.Ldc_I4,123);
b.Emit(OpCodes.Ldc_I4, 123);
b.Emit(OpCodes.Add);
b.Emit(OpCodes.Call, c);
b.Emit(OpCodes.Ret);
6.向動態方法傳參
var a = new DynamicMethod("Foo", typeof(int),
new Type[] { typeof(int),typeof(int)}, typeof(Program));
var b = a.GetILGenerator();
b.Emit(OpCodes.Ldarg_0);
b.Emit(OpCodes.Ldarg_1);
b.Emit(OpCodes.Add);
b.Emit(OpCodes.Ret);
int result = (int)a.Invoke(null, new object[] { 3,4});
Console.WriteLine(result);
7.生成局部變量
ILGenerator.DeclareLocal
var dynMeth = new DynamicMethod("Test", null, null, typeof(void));
var gen = dynMeth.GetILGenerator();
LocalBuilder localX = gen.DeclareLocal(typeof(int));
LocalBuilder localY = gen.DeclareLocal(typeof(int));
gen.Emit(OpCodes.Ldc_I4, 6); //將6壓入棧
gen.Emit(OpCodes.Stloc, localX); //將棧中的值給localX
gen.Emit(OpCodes.Ldc_I4, 7); //將7壓入棧
gen.Emit(OpCodes.Stloc, localY); //將棧中的值給localY
gen.Emit(OpCodes.Ldloc, localX); //將localX壓入棧
gen.Emit(OpCodes.Ldloc, localY); //將localY壓入棧
gen.Emit(OpCodes.Mul); //計算乘積
gen.Emit(OpCodes.Stloc, localX); //將乘積的值傳給localX
gen.EmitWriteLine(localX);
gen.Emit(OpCodes.Ret);
dynMeth.Invoke(null, null);
8.分支
ILGenerator.DefineLabel
ILGenerator.MarkLabel
Br(無分支條件),Brture(true則分支),Blt(第一個值小於第二個值則分支)
var dynMeth = new DynamicMethod("Test", null, null, typeof(void));
var gen = dynMeth.GetILGenerator();
Label startLoop = gen.DefineLabel();
Label endLoop = gen.DefineLabel();
LocalBuilder x = gen.DeclareLocal(typeof(int));
gen.Emit(OpCodes.Ldc_I4, 6); //將6壓入棧
gen.Emit(OpCodes.Stloc, x); //將棧中的值給X
gen.MarkLabel(startLoop); //開啓一個分支
gen.Emit(OpCodes.Ldc_I4, 10);
gen.Emit(OpCodes.Ldloc, x);
gen.Emit(OpCodes.Blt, endLoop); //跳到結束標記
gen.EmitWriteLine(x);
gen.Emit(OpCodes.Ldloc, x);
gen.Emit(OpCodes.Ldc_I4, 1);
gen.Emit(OpCodes.Add);
gen.Emit(OpCodes.Stloc, x);
gen.Emit(OpCodes.Br, startLoop);
gen.MarkLabel(endLoop); //分支結束標記
gen.Emit(OpCodes.Ret);
dynMeth.Invoke(null, null);
9.實例化對象與調用實例方法
ILGenerator.Newobj
OpCodes.Call:調用靜態方法和值類型的實例方法
OpCodes.Callvirt:調用引用類型的實例方法
var dynMeth = new DynamicMethod("Test", null, null, typeof(void));
var gen = dynMeth.GetILGenerator();
ConstructorInfo ci = typeof(StringBuilder).GetConstructor(new Type[0]);
gen.Emit(OpCodes.Newobj, ci);
gen.Emit(OpCodes.Callvirt, typeof(StringBuilder).GetProperty("MaxCapacity").GetGetMethod());
gen.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new[] { typeof(int) }));
gen.Emit(OpCodes.Ret);
dynMeth.Invoke(null, null);
var dynMeth = new DynamicMethod("Test", null, null, typeof(void));
var gen = dynMeth.GetILGenerator();
ConstructorInfo ci = typeof(StringBuilder).GetConstructor(new[] { typeof(string),typeof(int)});
gen.Emit(OpCodes.Ldstr, "Hello");
gen.Emit(OpCodes.Ldc_I4, 1000);
gen.Emit(OpCodes.Newobj, ci);
Type[] strT = { typeof(string) };
gen.Emit(OpCodes.Ldstr, ", world");
gen.Emit(OpCodes.Call, typeof(StringBuilder).GetMethod("Append", strT));
gen.Emit(OpCodes.Callvirt, typeof(object).GetMethod("ToString"));
gen.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", strT));
gen.Emit(OpCodes.Ret);
dynMeth.Invoke(null, null);
10.異常處理
ILGenerator.BeginExceptionBlock
ILGenerator.BeginCatchBlock
ILGenerator.BeginFinalBlock
ILGenerator.EndExceptionBlock
var dynMeth = new DynamicMethod("Test", null, null, typeof(void));
var gen = dynMeth.GetILGenerator();
MethodInfo emi = typeof(NotSupportedException).GetProperty("Message").GetGetMethod();
MethodInfo cmi = typeof(Console).GetMethod("WriteLine",new [] { typeof(object)});
gen.BeginExceptionBlock();
ConstructorInfo ci = typeof(NotSupportedException).GetConstructor(new Type[0]);
gen.Emit(OpCodes.Newobj, ci);
gen.Emit(OpCodes.Throw);
gen.BeginCatchBlock(typeof(NotSupportedException));
gen.Emit(OpCodes.Callvirt, emi);
gen.Emit(OpCodes.Call, cmi);
gen.BeginFinallyBlock();
gen.EmitWriteLine("Finally");
gen.EndExceptionBlock();
gen.Emit(OpCodes.Ret);
dynMeth.Invoke(null, null);
生成程序集和類型
保存生成的程序集
有些方法.Net Framework和.Net Core是不一樣的
AppDomain domain = AppDomain.CurrentDomain;
AssemblyName name = new AssemblyName("MyEmissions");
name.Version = new Version(2, 13, 0, 1);
AssemblyBuilder assemblyBuilder = domain.DefineDynamicAssembly(name,
AssemblyBuilderAccess.RunAndSave);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule",
"MyEmissions.dll");
TypeBuilder tb = moduleBuilder.DefineType("Widget", TypeAttributes.Public);
MethodBuilder methodBuilder = tb.DefineMethod("SayHello", MethodAttributes.Public, null, null);
ILGenerator gen = methodBuilder.GetILGenerator();
gen.EmitWriteLine("Hello World!");
gen.Emit(OpCodes.Ret);
tb.CreateType(); //必須要加
assemblyBuilder.Save("MyEmissions.dll");
2.在CreateType之前,類型屬於未創建狀態
3.動態生成的程序集可以保存成爲文件,該文件可以是dll或者exe,一旦保存成文件後,就可以以靜態的方式調用
生成類型成員
1.生成方法
MethodBuilder mb = tb.DefineMethod("SquareRoot",
MethodAttributes.Static | MethodAttributes.Public,
CallingConventions.Standard, typeof(double), new[] { typeof(double) });
ILGenerator gen2 = mb.GetILGenerator();
gen2.Emit(OpCodes.Ldarg_0);
gen2.Emit(OpCodes.Call,typeof(Math).GetMethod("Sqrt"));
gen2.Emit(OpCodes.Ret);
Type realType = tb.CreateType();
double x = (double)tb.GetMethod("SquareRoot").Invoke(null, new object[] { 10.0 });
Console.WriteLine(x);
2.生成字段和屬性
TypeBuilder tb = moduleBuilder.DefineType("Widget", TypeAttributes.Public);
FieldBuilder field = tb.DefineField("length", typeof(int), FieldAttributes.Private);
FieldBuilder field1 = tb.DefineField("_text", typeof(string), FieldAttributes.Private);
PropertyBuilder pb = tb.DefineProperty("Text", PropertyAttributes.None, typeof(string), new Type[0]);
MethodBuilder getter = tb.DefineMethod("get_text", MethodAttributes.Public | MethodAttributes.SpecialName,
typeof(string), new Type[0]);
ILGenerator gen = getter.GetILGenerator();
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Ldfld, field);
gen.Emit(OpCodes.Ret);
MethodBuilder setter = tb.DefineMethod("set_text", MethodAttributes.Assembly | MethodAttributes.SpecialName,
null, new[] { typeof(string)});
ILGenerator gen2 = setter.GetILGenerator();
gen2.Emit(OpCodes.Ldarg_0);
gen2.Emit(OpCodes.Ldarg_1);
gen2.Emit(OpCodes.Stfld, field);
gen2.Emit(OpCodes.Ret);
pb.SetGetMethod(getter);
pb.SetSetMethod(setter);
Type t = tb.CreateType();
object o = Activator.CreateInstance(t);
t.GetProperty("Text").SetValue(o, "Good", new object[0]);
string text = (string)t.GetProperty("Text").GetValue(o, null);
Console.WriteLine(text);
3.生成構造器
TypeBuilder tb = moduleBuilder.DefineType("Widget", TypeAttributes.Public);
FieldBuilder field = tb.DefineField("_capacity", typeof(int), FieldAttributes.Private);
ConstructorBuilder c = tb.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard
, new Type[0]);
ILGenerator gen = c.GetILGenerator();
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Ldc_I4,4000);
gen.Emit(OpCodes.Stfld, field);
gen.Emit(OpCodes.Ret);