C# 反射詳解一

首先反射是基於System.Reflection命名空間下,.Net框架提供的幫助類庫,可以讀取並使用metadata(元數據:描述對象信息的數據).

我們再來看下代碼生成編譯的總過程。

編譯器編譯(一次編譯):類庫生成的都是dll,控制檯生成的是exe文件

 dll和exe都包含兩大塊metadata和IL(中間語言)

dll和exe的運行環境依賴於CLR,我們安裝.net frmework時會自動配置CLR和JIT(及時編譯器)

JIT(二次編譯(編譯之後,可以在不同平臺使用))會將dll和exe文件轉爲機器碼

反射基礎 

三種加載dll程序集的方法

//根據dll名稱加載,不帶dll後綴
Assembly assembly = Assembly.Load("Bussiness");
//完整路徑加載,注意web層要引用加載的Bussiness層,如果沒有依賴項,使用時會報錯
Assembly assembly1 = Assembly.LoadFile(@"E:\反射\Rel\bin\Debug\netcoreapp3.1\Bussiness.dll");
//根據dll名稱加載,帶dll後綴
Assembly assembly2 = Assembly.Load("Bussiness.dll");

//獲取該dll中的模塊信息
foreach (var item in assembly.GetModules())
{
    Console.WriteLine(item.FullyQualifiedName);
    // => E:\反射\Rel\bin\Debug\netcoreapp3.1\Bussiness.dll
}
//獲取該dll中的類型信息
foreach (var item in assembly.GetTypes())
{
    Console.WriteLine(item.FullName);
    // => Bussiness.Class1
    // => Bussiness.Method
}

這是Bussiness層信息

 根據反射創建對象

//加載程序集
Assembly assembly3 = Assembly.Load("Common");
//獲取指定類型對象的(類型)信息
Type type = assembly3.GetType("Common.StringHelper");
//創建對象
object o = Activator.CreateInstance(type);

Common層內容

 當我們創建對象時,會打印 StringHelper被構造了! 因爲每一個類都 自帶一個構造函數,間接執行了WriteLine語句。

但是當我們調用QueryString方法時就報錯了

 原因是編譯器不認可,編譯器只把他當成是一個onject對象。

我們可以這樣寫

//加載程序集
Assembly assembly3 = Assembly.Load("Common");
//獲取指定類型對象的(類型)信息
Type type = assembly3.GetType("Common.StringHelper");
//創建對象
dynamic o = Activator.CreateInstance(type);
o.QueryString("今天真熱");
dynamic會避開編譯器的檢查,相當於弱語言類型,從而達到我們的預想.
對此我們可以加以封裝一下
public static StringHelper CreateHelper()
{
    //加載程序集
    Assembly assembly3 = Assembly.Load("Common");
    //獲取指定類型對象的(類型)信息
    Type type = assembly3.GetType("Common.StringHelper");
    //創建對象
    dynamic o = Activator.CreateInstance(type);
    return o;
}

//=>調用
/*
StringHelper stringHelper = Factory.CreateHelper();
await stringHelper.QueryString("開空調");
*/

反射破壞單例:單例類在內存中是唯一的,本來是不能實例化,但是可通過反射來創建對象,從而調用其私有構造函數。

public sealed class Single
{
    private Single()
    {
        Console.WriteLine($"{this.GetType().Name}已被構造");
    }
  
    private static Single single = null;

    public static Single CreateInstance()
    {
        if (single == null)
        {
            single = new Single();
        }
        return single;
    }
}
//加載程序集
Assembly assembly3 = Assembly.Load("Common");
//獲取指定類型對象的(類型)信息
Type type = assembly3.GetType("Common.Single");
//創建對象
Common.Single single = (Common.Single)Activator.CreateInstance(type, true);

根據參數調用不同的構造函數

public class TestClass
{
    public TestClass(string parameter)
    {
        Console.WriteLine($"{this.GetType().Name}已被構造");
    }
    public TestClass(int parameter)
    {
        Console.WriteLine($"{this.GetType().Name}已被構造");
    }
}
//加載程序集
Assembly assembly4 = Assembly.Load("Common");
//獲取指定類型對象的(類型)信息
Type type = assembly4.GetType("Common.TestClass");
//創建對象  這裏有一個參數數組,會根據參數的順序和類型匹配對應的構造函數,如果參數是空數組或null,則構造函數不接受任何參數(無參數構造函數)
Common.TestClass single = (Common.TestClass)Activator.CreateInstance(type, 1);
Common.TestClass single1 = (Common.TestClass)Activator.CreateInstance(type, "1");

反射操作泛型

public class GenericClass<T, R, E>
{
    public void Show(T t, R r, E e)
    {
        Console.WriteLine($"{t.GetType().Name},{r.GetType().Name},{e.GetType().Name}");
    }
}
//加載程序集
Assembly assembly4 = Assembly.Load("Common");
//獲取指定類型對象的(類型)信息  注意:`3是佔位符,該泛型對象中有幾個泛型參數
Type type = assembly4.GetType("Common.GenericClass`3");
//不能像這樣創建對象,因爲泛型對象在實例化的時候要指定參數及類型
//object o = Activator.CreateInstance(type);
//指定泛型對象的參數類型
Type type1 = type.MakeGenericType(new Type[] { typeof(decimal), typeof(Guid), typeof(Enum) });
object oGeneric = Activator.CreateInstance(type1);

調用無參方法

public class ReflectionTest
{
    public void show1()
    {
        Console.WriteLine($"調用了{this.GetType()}中的方法show1");
    }
}
//加載程序集
Assembly assembly4 = Assembly.Load("Common");
//獲取指定類型對象的(類型)信息
Type type = assembly4.GetType("Common.ReflectionTest");
//創建對象
object o = Activator.CreateInstance(type);
MethodInfo methodInfo = type.GetMethod("show1");
//調用方法,第一個參數是實例類型,第二個是參數列表
methodInfo.Invoke(o, null);

有參方法的調用

public void show2(int i)
{
    Console.WriteLine($"調用了{this.GetType()}中的方法show2");
}
//加載程序集
Assembly assembly4 = Assembly.Load("Common");
//獲取指定類型對象的(類型)信息
Type type = assembly4.GetType("Common.ReflectionTest");
//創建對象
object o = Activator.CreateInstance(type);
MethodInfo methodInfo = type.GetMethod("show2");
//第一個參數是實例類型,第二個是參數列表
methodInfo.Invoke(o, new object[] { 1 });

靜態方法的調用

public static void show3(int i)
{
    Console.WriteLine($"調用了{typeof(ReflectionTest)}中的方法show3");
}
//加載程序集
Assembly assembly4 = Assembly.Load("Common");
//獲取指定類型對象的(類型)信息
Type type = assembly4.GetType("Common.ReflectionTest");
//創建對象
object o = Activator.CreateInstance(type);
MethodInfo methodInfo = type.GetMethod("show3");
//第一個參數是實例類型,第二個是參數列表
//因爲是靜態方法,所以不需要傳實例對象(可傳可不傳),靜態方法是可以通過方法名直接訪問的
methodInfo.Invoke(null, new object[] { 1 });

重載方法的調用

public static void show4()
{
    Console.WriteLine($"調用了{typeof(ReflectionTest)}中的方法show4");
}

public static void show4(int i)
{
    Console.WriteLine($"調用了{typeof(ReflectionTest)}中的方法show4");
}

public static void show4(string s)
{
    Console.WriteLine($"調用了{typeof(ReflectionTest)}中的方法show4");
}
public static void show4(string s, int i)
{
    Console.WriteLine($"調用了{typeof(ReflectionTest)}中的方法show4");
}
//加載程序集
Assembly assembly4 = Assembly.Load("Common");
//獲取指定類型對象的(類型)信息
Type type = assembly4.GetType("Common.ReflectionTest");
//創建對象
object o = Activator.CreateInstance(type);
MethodInfo methodInfo = type.GetMethod("show4", new Type[] { });
//第一個參數是實例類型,第二個是參數列表
//因爲是靜態方法,所以不需要傳實例對象(可傳可不傳),靜態方法是可以通過方法名直接訪問的
methodInfo.Invoke(null, new object[] { });
{
    MethodInfo methodInfo1 = type.GetMethod("show4", new Type[] { typeof(int) });
    methodInfo1.Invoke(null, new object[] { 1 });
}
{
    MethodInfo methodInfo2 = type.GetMethod("show4", new Type[] { typeof(string) });
    methodInfo2.Invoke(null, new object[] { "1" });
}
{
    MethodInfo methodInfo3 = type.GetMethod("show4", new Type[] { typeof(string), typeof(int) });
    methodInfo3.Invoke(null, new object[] { "1", 1 });
}
私有方法的調用
private void show5(string s)
{
    Console.WriteLine($"調用了{typeof(ReflectionTest)}中的方法show5");
}
//加載程序集
Assembly assembly4 = Assembly.Load("Common");
//獲取指定類型對象的(類型)信息
Type type = assembly4.GetType("Common.ReflectionTest");
//創建對象
object o = Activator.CreateInstance(type);
MethodInfo methodInfo = type.GetMethod("show5", BindingFlags.Instance | BindingFlags.NonPublic);
//第一個參數是實例類型,第二個是參數列表
methodInfo.Invoke(o, new object[] { "1" });

調用泛型類中的方法

public void Show(T t, R r, E e)
{
    Console.WriteLine($"{t.GetType().Name},{r.GetType().Name},{e.GetType().Name}");
}
//加載程序集
Assembly assembly4 = Assembly.Load("Common");
//獲取指定類型對象的(類型)信息 注意:`3是佔位符,該泛型對象中有幾個泛型參數
Type type = assembly4.GetType("Common.GenericClass`3");
//指定泛型對象的參數類型
Type type1 = type.MakeGenericType(new Type[] { typeof(double), typeof(Guid), typeof(DateTime) });
object oGeneric = Activator.CreateInstance(type1);
MethodInfo methodInfo = type1.GetMethod("Show");
methodInfo.Invoke(oGeneric, new object[] { 1.02, Guid.NewGuid(), DateTime.Now });

調用泛型類中的泛型方法

public class GenericClass<T, R, E>
{
    public void Show<X>(T t, R r, E e, X x)
    {
        Console.WriteLine($"{t.GetType().Name},{r.GetType().Name},{e.GetType().Name},{x.GetType().Name}");
    }
}
//加載程序集
Assembly assembly4 = Assembly.Load("Common");
//獲取指定類型對象的(類型)信息 注意:`3是佔位符,該泛型對象中有幾個泛型參數
Type type = assembly4.GetType("Common.GenericClass`3");
//指定泛型對象的參數類型
Type type1 = type.MakeGenericType(new Type[] { typeof(double), typeof(Guid), typeof(DateTime) });
object oGeneric = Activator.CreateInstance(type1);
MethodInfo methodInfo = type1.GetMethod("Show");
//指定泛型方法的參數類型 MethodInfo methodInfo1
= methodInfo.MakeGenericMethod(new Type[] { typeof(string) }); methodInfo1.Invoke(oGeneric, new object[] { 1.2, Guid.NewGuid(), DateTime.Now, "" });

反射操作實體字段、屬性

public class People
{
    public People()
    {
        Console.WriteLine($"{this.GetType().FullName}構造了一次");
    }

    public int id { get; set; }
    public string name { get; set; }
    public string description { get; set; }
}
Type type = typeof(People);
object o = Activator.CreateInstance(type);
//type.GetFields()也可以,操作也是一樣的
foreach (var item in type.GetProperties())
{
    //Console.WriteLine(item.Name);//打印字段屬性名
    //Console.WriteLine(item.GetValue(o));//打印字段值
    if (item.Name.Equals("id"))
        item.SetValue(o, 1);//設置字段值
    if (item.Name.Equals("name"))
        item.SetValue(o, "111");
    Console.WriteLine($"{type.Name}.{item.Name}={item.GetValue(o)}");
}

反射映射字段,對象中字段數據轉換賦值

public class People
{
    public People()
    {
        Console.WriteLine($"{this.GetType().FullName}構造了一次");
    }

    public int id { get; set; }
    public string name { get; set; }
    public string description { get; set; }
}

public class DtoPeople
{
    public DtoPeople()
    {
        Console.WriteLine($"{this.GetType().FullName}構造了一次");
    }

    public int id { get; set; }
    public string name { get; set; }
    public string description { get; set; }
}
People people = new People();
people.id = 1;
people.name = "1";
people.description = "";

Type typePeople = typeof(People);
Type typeDtoPeople = typeof(DtoPeople);
//需要賦值的對象
object o = Activator.CreateInstance(typeDtoPeople); foreach (var item in typeDtoPeople.GetFields()) { //找出源類型字段的值 item.Name爲字段屬性名 object value = typePeople.GetProperty(item.Name).GetValue(people); //賦值 item.SetValue(o, value); }

反射的基礎就寫到這兒,接下來幾篇會利用反射寫自定義ORM與IOC組件.

反射的優點:動態獲取對象.可擴展

反射的缺點:1.寫起來複雜.2.避開編譯器的檢查,錯誤風險很大.3.性能不好

bye-bye.

 Grinning Cat with Smiling Eyes

       


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章