主頁 > .NET開發 > C#反射

C#反射

2020-11-12 19:30:57 .NET開發

1.What?反射是什么?

反射:無處不在,MVC,Webfrom,asp.net,ORM,IOC,AOP,幾乎所有的框架都離不開反射,那么反射到底是什么?

我們寫的代碼,計算機要識別,需要二次編譯,中間會經過編譯器編譯,得到dll,exe,再被JIT編譯最終被計算機語言識別,執行,那dll,exe是怎么生成的呢?

生成的exe還可以直接打開執行

 那這個exe里面具體是什么呢?我們可以用反編譯工具打開看看,打開就可以看到我們反編譯以后的IL:也是一種面向物件語言,但是不太好閱讀

反射Reflection:system.Reflection,其實就是.net.fromwork提供的一個幫助內庫,可以讀取并使用metadata

那我們如何去讀取資訊呢?

2.用反射加載和讀取資訊[三種方式]

我們先隨便寫個介面類:新建-DB.Interface類別庫,新建一個介面類IDBHelper

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace DB.Interface
 8 {
 9     // DB.Interface介面類別庫
10     public interface IDBHelper
11     {
12         void Query();
13     }
14 }

再新建一個DB.MySql類別庫,新建一個MySqlHelper類繼承介面IDBHelper

using DB.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DB.MySql
{
    public class MySqlHelper : IDBHelper
    {
        /// <summary>
        /// MySqlHelper建構式
        /// </summary>
        public MySqlHelper()
        {
            Console.WriteLine("{0}被構造", this.GetType().Name);
        }
        /// <summary>
        /// Query方法
        /// </summary>
        public void Query()
        {
            Console.WriteLine("{0}.Query",this.GetType().Name);
        }
    }
}
MySqlHelper

按照上面的步驟,新建一個DB.SqlServer類別庫,新建一個SqlServerHelper類繼承介面IDBHelper

using DB.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DB.SqlServer
{
    /// <summary>
    /// SqlServer實作
    /// </summary>
    public class SqlServerHelper : IDBHelper
    {
        /// <summary>
        /// SqlServerHelper建構式
        /// </summary>
        public SqlServerHelper()
        {
            Console.WriteLine("{0}被構造",this.GetType().Name);
        }
        /// <summary>
        /// Query方法
        /// </summary>
        public void Query()
        {
            Console.WriteLine("{0}.Query",this.GetType().Name);
        }
    }
}
SqlServerHelper
                #region Reflecgion
                {
                    Console.WriteLine("---------------------------------------------反射加載和讀取資訊---------------------------------------------------------");
                    //動態加載 都是把dll加到記憶體里面去,不需要向上面的普通方法一樣參考
                    //1.一個完整的dll名稱,不需要后綴,目的:把dll加載到記憶體中間去;
                    //缺陷:但是有個限制從編譯以后生成的exe所在的路徑去查找,既可以找dll,又可以找exe,兩個一樣得話先找dll【常用】
                    Assembly assembly = Assembly.Load("DB.MySql");
                    //2.還有一種加載方式,path,需要一個完整的路徑,性能差不多,也可以切換為別的參考了的路徑【全名稱= 全路徑+dll名稱 + 后綴】
                    Assembly assembly1 = Assembly.LoadFile(@"F:\LearnTest\MyReflectionFirst\MyReflectionFirst\bin\Debug\DB.MySql.dll");
                    //3.loadfrom:帶后綴的,當前路徑查找,也可以全路徑
                    Assembly assembly2 = Assembly.LoadFrom("DB.MySql.dll");//【當前路徑+ 后綴】
                    Assembly assembly3 = Assembly.LoadFrom(@"F:\LearnTest\MyReflectionFirst\MyReflectionFirst\bin\Debug\DB.MySql.dll");

                    //讀取里面的資訊
                    foreach (var types in assembly.GetTypes())//GetTypes 獲取此程式集中的全部型別
                    {
                        Console.WriteLine(types.Name);
//type.IsGenericType //判斷當前是否是泛型類
foreach (var method in types.GetMethods())//回傳為GetMethods的所有公共方法 { Console.WriteLine(method.Name); } } } #endregion

 我們跟蹤就可以看到利用反射加載出來的一些基本資訊

3.反射使用資訊

                #region Common
                {
                    Console.WriteLine("---------------------------------------------Common---------------------------------------------------------");
                    {
                        //添加介面參考,內庫參考,實體化并且呼叫方法
                        IDBHelper dBHelper = new MySqlHelper();
                        dBHelper.Query();
                    }
                }
                #endregion
                #region Reflecgion
                {
                    Console.WriteLine("---------------------------------------------用反射使用資訊---------------------------------------------------------");
                    //1.動態加載
                    Assembly assembly = Assembly.Load("DB.MySql");
                    // 第一步我們會獲取到全部的型別,也可以指定獲取型別【完整型別名稱】
                    Type type = assembly.GetType("DB.MySql.MySqlHelper");
                    //3.實體化
                     // 意思跟Activator.CreateInstance(type);差不多都是實體化new MySqlHelper();但是這邊創建的實體是個object型別
                    object oDBHelper=Activator.CreateInstance(type);
                    //很確定oDBHelper里面有Query這個方法Why不能直接呼叫方法?編譯器不認可,不能直接呼叫
                    //c#是一門強型別語言,靜態語言,編譯時就確定好了型別確保安全
                    //oDBHelper.Query()
                    //dynamic可以是因為它很特殊編譯器不檢查,運行時才檢查
                    //dynamic dDBHelper = Activator.CreateInstance(type);
                    //dDBHelper.Query();

                    //4.型別轉換,型別不對會直接回傳null
                    IDBHelper dBHelper = oDBHelper as IDBHelper;
                    //5.方法呼叫
                    dBHelper.Query();
                }
                #endregion

那么問題來了我只需要用普通方法兩句話寫完的,我們為什么要用反射要寫5句???何必呢?意義何在?

我們就上面的代碼先來封裝一下看看:

1 <configuration>
2     <startup> 
3         <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
4     </startup>
5     <appSettings>
6         <!--以英文逗號","分開-->
7         <add key="IDBHelperConfig" value=https://www.cnblogs.com/wangwangwangMax/archive/2020/11/12/"DB.MySql.MySqlHelper,DB.MySql"/>
8 </configuration>
namespace MyReflectionFirst
{
    public class SingleFactory
    {
        /// <summary>
        /// private避免被人可以看到,static靜態,會在程式第一次呼叫的時候讀取,只讀一遍
        /// </summary>
        private static string IDBHelperConfig = ConfigurationManager.AppSettings["IDBHelperConfig"].ToString();
        private static string TypeName = IDBHelperConfig.Split(',')[0];
        private static string DLLName = IDBHelperConfig.Split(',')[1];
        public static IDBHelper CreateInstance()
        {
            Assembly assembly = Assembly.Load(DLLName);
            Type type = assembly.GetType(TypeName);
            object oDBHelper = Activator.CreateInstance(type);
            IDBHelper dBHelper = oDBHelper as IDBHelper;
            return dBHelper;
        }
    }
}

那我們再來呼叫看看:

                {
                    Console.WriteLine("-------------------------------------Reflecgion+Factory+Config-------------------------------------------------");
                    IDBHelper dBHelper = SingleFactory.CreateInstance();
                    dBHelper.Query();
                }

這是普通方法和反射封裝完成呼叫 的結果以及代碼對比,從呼叫上是不是差不多了呢?

我們再進一步對比延伸擴展一下:

如果我們需要更換一下版本呢?

普通方法:【就必須重新參考,修改代碼,重新編譯發布

                        //如果從MySqlHelper換成SqlServerHelper,就必須重新參考,修改代碼,重新編譯發布
                        IDBHelper dBHelper = new SqlServerHelper();
                        dBHelper.Query();

而我們的反射,只需修改組態檔即可:

                      <add key="IDBHelperConfig" value=https://www.cnblogs.com/wangwangwangMax/archive/2020/11/12/"DB.SqlServer.SqlServerHelper,DB.SqlServer"/>

 結果:

那要是我們要新增一個以前沒有的Oracle版本:

按照上面的步驟,新建一個DB.Oracle類別庫,新建一個OracleHelper類繼承介面IDBHelper

using DB.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DB.Oracle
{
    public class OracleHelper : IDBHelper
    {
        public OracleHelper() 
        {
            Console.WriteLine("{0}被構造",this.GetType().Name);
        }
        public void Query()
        {
            Console.WriteLine("{0}.Query",this.GetType().Name);
        }

    }
}
OracleHelper

 用反射,通過修改組態檔就可以達到以下結果:

                  <add key="IDBHelperConfig" value=https://www.cnblogs.com/wangwangwangMax/archive/2020/11/12/"DB.Oracle.OracleHelper,DB.Oracle"/>

結果:

//特點:動態擴展性太強
//可配置可擴展:完全不修改原有代碼,只是增加新的實作,copy,修改組態檔,就可以支持新功能
//原理:反射的動態加載和動態創建物件,以及組態檔結合
//可以支持隨機換,也可以后面新增
//前提:實作類必須是事先已有的,而且在目錄下面的

4.反射的使用

上面我們用反射獲取了無引數的建構式,那么我們怎么去獲取有引數的建構式呢?我們怎么去獲取建構式引數,以及型別,呼叫?

 讓我們看一個例子,在DB.SqlServer類別庫中,新建一個ReflecgionTest類

    public class ReflecgionTest
    {
        #region Identity
        public ReflecgionTest()
        {
            Console.WriteLine("這是{0}無引數建構式", this.GetType());
        }
        public ReflecgionTest(string name)
        {
            Console.WriteLine("這是{0}有引數建構式", this.GetType());
        }
        public ReflecgionTest(int id)
        {
            Console.WriteLine("這是{0}有引數建構式", this.GetType());
        }
        #endregion
    }
                {
                    Console.WriteLine("-----------------------------------ctor&parameter---------------------------------------------------");
                    Assembly assembly = Assembly.Load("DB.SqlServer");
                    Type type = assembly.GetType("DB.SqlServer.ReflecgionTest");
                    //獲取建構式
                    foreach (ConstructorInfo ctor in type.GetConstructors())//回圈訪問集合或陣列的名稱
                    {
                        Console.WriteLine(ctor.Name);
                        //獲取建構式引數
                        foreach (var parameter in ctor.GetParameters())
                        {
                            //獲取建構式引數型別
                            Console.WriteLine(parameter.ParameterType);
                        }
                    }

                    //怎么為不同的建構式指定不同的引數型別
                    //object test1 = Activator.CreateInstance(type);
                    //object test2 = Activator.CreateInstance(type, "123");
                    //object test3 = Activator.CreateInstance(type, 123);
                    //應用建議改為以下寫法
                    object test4 = Activator.CreateInstance(type, new object[] { "123" });
                    object test5= Activator.CreateInstance(type, new object[] { 123});
                }

結果:

5.反射-黑科技:反射破壞單例

讓我們看一個例子,在DB.SqlServer類別庫中,新建一個Singleton類

namespace DB.SqlServer
{
    /// <summary>
    /// 單例模式:就是一個類,保證在整個行程中就只有一個實體
    /// </summary>
    public sealed class Singleton
    {
        //3.當然這個實體只能有一個,所以我們提供了一個靜態欄位,
        private static Singleton _singleton = null;
        //1.建構式私有化,才不能隨便new,才能保證只有一個物件
        private Singleton()
        {
            Console.WriteLine("{0}被構造",this.GetType().Name);
        }
        //4.這個欄位只能初始化一次,那怎么初始化?我們交給了這個靜態建構式,它由CLR保障,在程式啟動的第一次呼叫這個類Singleton之前完成的呼叫,而且只呼叫一次
        static Singleton()
        {
            _singleton = new Singleton();
        }
        //2.私有化了之后,對外不能new,但我可以對外提供一個公開的靜態方法,負責提供這個物件的實體,可以呼叫這個方法獲取這個實體,那怎么獲取?
        public static Singleton GetInstance()
        {
            return _singleton;
        }
    }
}
                {
                    Console.WriteLine("-----------------------------------擴展:未使用反射之前的單例呼叫---------------------------------------------------");
                    //Singleton singleton = new Singleton();//私有化不能直接構造
                    Singleton singleton1 = Singleton.GetInstance();
                    Singleton singleton2 = Singleton.GetInstance();
                    Singleton singleton3 = Singleton.GetInstance();
                    Console.WriteLine($"{Object.ReferenceEquals(singleton1, singleton3)}");//不管有幾個實體,結果肯定為True,因為都是同一個物件,只會被構造一次
                }
                {
                    Console.WriteLine("-----------------------------------擴展:反射破壞單例---反射呼叫私有建構式---------------------------------------------------");
                    Assembly assembly = Assembly.Load("DB.SqlServer");
                    Type type = assembly.GetType("DB.SqlServer.Singleton");
                    //Singleton singletonA=(Singleton)Activator.CreateInstance(type);//型別轉換
                    //如上寫法會報錯,因為上面我們說過它相當于一個實體化,但是這個建構式私有化,不能直接New
                    Singleton singletonA = (Singleton)Activator.CreateInstance(type,true);//但是反射做到了這件事
                    //Why這個地方執行了第一次使用的時候會執行兩遍建構式,程式在一次使用這個類的時候就會去完成靜態建構式的構造,程式在每次使用都會再去構造一次
                    Singleton singletonB = (Singleton)Activator.CreateInstance(type, true);
                    Singleton singletonC = (Singleton)Activator.CreateInstance(type, true);
                    //nonPublic:如果公共或非公共默認建構式可以匹配,則為true;如果只有公共默認建構式可以匹配,則為false,
                    //所以物件是不一樣的,物件被構造了多次
                    Console.WriteLine($"{Object.ReferenceEquals(singletonA,singletonC)}");

                }

結果對比:

 六.反射呼叫泛型類與方法

讓我們看一個例子,在DB.SqlServer類別庫中,新建一個GenericTest類

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DB.SqlServer
{
    public class GenericTest<T,W,X>
    {
        public void Show(T t, W w,X x)
        {
            Console.WriteLine("This's t.type={0},w.type={1}, x.type={2}", t.GetType().Name,w.GetType().Name,x.GetType().Name);
        }
    }
    public class GenericMethod
    {
        public void Show<T, W, X>(T t, W w, X x)
        {
            Console.WriteLine("This's t.type={0},w.type={1}, x.type={2},t:{3},w:{4},x:{5}", t.GetType().Name, w.GetType().Name, x.GetType().Name, t, w, x);
        }
    }
    public class GenericDouble<T>
    {
        public void Show<W, X>(T t, W w, X x)
        {
            Console.WriteLine("This's t.type={0},w.type={1}, x.type={2},t:{3},w:{4},x:{5}", t.GetType().Name, w.GetType().Name, x.GetType().Name,t,w,x);
        }
    }
}
泛型
                {
                    Console.WriteLine("-----------------------------------擴展:反射和泛型---------------------------------------------------");
                    Assembly assembly = Assembly.Load("DB.SqlServer");
                    Type type = assembly.GetType("DB.SqlServer.GenericTest");
                    Object oGeneric = Activator.CreateInstance(type);
                }

按照之前的操作,我們發現這邊得到的type=null?Why?

//原因:因為public class GenericTest<T,W,X>它是泛型編譯之后會變為DB.SqlServer.GenericTest`3<T, W, X>【反編譯工具也可以看到】

// 我們需要把它修改為:
Type type = assembly.GetType("DB.SqlServer.GenericTest`3");

跟蹤結果已經找到了我們要的資料了:

 再執行,報錯了,型別找到了,為什么它不能創建這個實體?它不是有個無引數建構式嗎?為什么沒能創建一個實體?

 那這代碼現在應該如何修改呢?

                    //在泛型型別時,不能忘記我們創建物件,它是不能夠實體化的,【eg:GenericTest genericTest=new GenericTest()】那肯定是不行的
                    //起碼你得指定好型別才能夠實體化【eg:GenericTest<int, string, DateTime> genericTest = new GenericTest<int, string, DateTime>();】那應該怎樣寫呢?
                    Type typeMake = type.MakeGenericType(new Type[] {typeof(int),typeof(string),typeof(DateTime) });
                    //泛型類是一個不確定型別的,這句話就表示我把型別確定一下,你要什么引數型別,這邊是個type陣列,new Type[] {}陣列
                    Object oGeneric = Activator.CreateInstance(typeMake);//只有這個typeMake才可以創建,這邊不能強制轉換,因為需要型別

跟蹤的結果:

如果反射創建物件之后,知道方法名稱,怎么樣不做型別轉換,直接呼叫方法?讓我們直接下面的實體吧

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DB.SqlServer
{
    public class ReflecgionTest
    {
        #region Identity
        public ReflecgionTest()
        {
            Console.WriteLine("這是{0}無引數建構式", this.GetType());
        }
        public ReflecgionTest(string name)
        {
            Console.WriteLine("這是{0}有引數建構式", this.GetType());
        }
        public ReflecgionTest(int id)
        {
            Console.WriteLine("這是{0}有引數建構式", this.GetType());
        }
        #endregion

        #region Method
        /// <summary>
        /// 無參方法
        /// </summary>
        public void Show1()
        {
            Console.WriteLine("這里是{0}的Show1", this.GetType());
        }
        /// <summary>
        /// 有引數方法
        /// </summary>
        /// <param name="id"></param>
        public void Show2(int id)
        {
            Console.WriteLine("這里是{0}的Show2", this.GetType());
        }
        /// <summary>
        /// 多載方法之一
        /// </summary>
        /// <param name="id"></param>
        /// <param name="name"></param>
        public void Show3(int id, string name)
        {
            Console.WriteLine("這里是{0}的Show3,id:{1},string:{2}", this.GetType(),id,name);
        }
        /// <summary>
        /// 多載方法之二
        /// </summary>
        /// <param name="name"></param>
        /// <param name="id"></param>
        public void Show3(string name, int id)
        {
            Console.WriteLine("這里是{0}的Show3_2,string:{1},id:{2}", this.GetType(),name,id);
        }
        /// <summary>
        /// 多載方法之三
        /// </summary>
        /// <param name="id"></param>
        public void Show3(int id)
        {

            Console.WriteLine("這里是{0}的Show3_3,id:{1}", this.GetType(),id);
        }
        /// <summary>
        /// 多載方法之四
        /// </summary>
        /// <param name="name"></param>
        public void Show3(string name)
        {

            Console.WriteLine("這里是{0}的Show3_4,name:{1}", this.GetType(),name);
        }
        /// <summary>
        /// 多載方法之五
        /// </summary>
        public void Show3()
        {
            Console.WriteLine("這里是{0}的Show3_1", this.GetType());
        }
        /// <summary>
        /// 私有方法
        /// </summary>
        /// <param name="name"></param>
        private void Show4(string name)
        {
            Console.WriteLine("這里是{0}的Show4_私有方法呼叫", this.GetType());
        }
        /// <summary>
        /// 靜態方法
        /// </summary>
        /// <param name="name"></param>
        public static void Show5(string name)
        {
            Console.WriteLine("這里是{0}的Show5_靜態方法呼叫", typeof(ReflecgionTest));
        }
        #endregion
    }
}
ReflecgionTest.cs

反射呼叫:無引數,有引數,多載,靜態,私有方法實體

                {
                    Console.WriteLine("如果反射創建物件之后,知道方法名稱,怎么樣不做型別轉換,直接呼叫方法?");
                    Assembly assembly = Assembly.Load("DB.SqlServer");
                    Type type = assembly.GetType("DB.SqlServer.ReflecgionTest");
                    Object test = Activator.CreateInstance(type);
                    //主要是查看它里面的所有方法名,引數,引數型別
                    foreach (var metohd in type.GetMethods())
                    {
                        Console.WriteLine(metohd.Name+"");
                        foreach (var parameter in metohd.GetParameters())
                        {
                            Console.WriteLine($"{parameter.Name},{parameter.ParameterType.Name}");
                        }
                    }
                    //反射呼叫無引數的方法
                    {
                        MethodInfo methodInfo = type.GetMethod("Show1");
                        methodInfo.Invoke(test, null);//第一個引數表示實體,后一個引數串列
                    }
                    //反射呼叫帶引數的方法
                    {
                        MethodInfo methodInfo1 = type.GetMethod("Show2");
                        methodInfo1.Invoke(test, new object[] { 123 });
                    }
                    //多載方法呼叫
                    {
                        MethodInfo method=type.GetMethod("Show3",new Type[] { typeof(string),typeof(int)});
                        method.Invoke(test, new object[] { "現在是哪一年", 2020});
                    }
                    {
                        MethodInfo method = type.GetMethod("Show3",new Type[] { typeof(int),typeof(string)});
                        method.Invoke(test, new object[] { 2020, "year" });
                    }
                    {
                        MethodInfo method = type.GetMethod("Show3",new Type[] { typeof(int)});
                        method.Invoke(test, new object[] { 2020 });
                    }
                    {
                        MethodInfo method = type.GetMethod("Show3",new Type[] { typeof(string)});
                        method.Invoke(test, new object[] { "wang" });
                    }
                    {
                        MethodInfo method = type.GetMethod("Show3",new Type[] { });
                        method.Invoke(test, null);
                    }
                    //靜態方法【特別】:實體可以要,也可以不要【null】
                    {
                        MethodInfo method = type.GetMethod("Show5");
                        method.Invoke(null,new object[] { "你好"});
                    }
                    {
                        MethodInfo method = type.GetMethod("Show5");
                        method.Invoke(test, new object[] { "你好" });
                    }
                    {//反射呼叫私有方法
                        var method = type.GetMethod("Show4", BindingFlags.Instance | BindingFlags.NonPublic);
                        method.Invoke(test, new object[] { "私有方法呼叫" });
                    }
                }

結果:

  那么泛型的方法又如何用反射呼叫呢?

                {
                    Console.WriteLine("-----------------------------------擴展:泛型方法GenericMethod---------------------------------------------------");
                    Assembly assembly = Assembly.Load("DB.SqlServer");
                    Type type = assembly.GetType("DB.SqlServer.GenericMethod");
                    Object oGeneric = Activator.CreateInstance(type);
                    MethodInfo method = type.GetMethod("Show");//泛型方法不用Show`3
                    var methodnew=method.MakeGenericMethod(new Type[]{ typeof(int),typeof(string), typeof(DateTime)});
                    methodnew.Invoke(oGeneric, new object[] { 12, "泛型方法 ", DateTime.Now });
                }

結果:

泛型方法和泛型類同時存在的情況:

                {
                    Console.WriteLine("擴展:泛型類+泛型方法GenericMethod");
                    Assembly assembly = Assembly.Load("DB.SqlServer");
                    Type type = assembly.GetType("DB.SqlServer.GenericDouble`1").MakeGenericType(new Type[] { typeof(int) });
                    Object oObject=Activator.CreateInstance(type);
                    MethodInfo method = type.GetMethod("Show").MakeGenericMethod(new Type[] {typeof(string),typeof(DateTime) });
                    method.Invoke(oObject,new object[] { 2020,"hello",DateTime.Today});
                }

結果:

 七:反射的應用:

獲取全部的物體的全部屬性并給其賦值:

//物體類不考慮擴展問題,沒意義
                    Console.WriteLine("---------------------------------------------Reflection---------------------------------------------------------");
                    Type type = typeof(People);
                    Object opeople=Activator.CreateInstance(type);
                    //獲取全部的屬性
                    foreach (var prop in type.GetProperties())
                    {
                        Console.WriteLine($"{type.Name}.{prop.Name}={prop.GetValue(opeople)}");
                        //給prop里面的屬性增加一個值
                        if (prop.Name.Equals("Id"))
                        {
                            prop.SetValue(opeople, 1);
                        }
                        else if(prop.Name.Equals("Name"))
                        {
                            prop.SetValue(opeople, "泛型物體屬性");
                        }
                        Console.WriteLine($"{type.Name}.{prop.Name}={prop.GetValue(opeople)}");
                    }
                    foreach (var field in type.GetFields())
                    {
                        Console.WriteLine($"{type.Name}.{field.Name}={field.GetValue(opeople)}");
                        if (field.Name.Equals("Description"))
                        {
                            field.SetValue(opeople, "只是一個欄位");
                        }
                        Console.WriteLine($"{type.Name}.{field.Name}={field.GetValue(opeople)}");
                    }
//思考:反射對物體層的操作如此麻煩,Why我們還要用它?eg:讓我們一起看一個場景:
        public Company FindOld(int Id)
        {
            string sql = @"SELECT  [Id]
                                    ,[Name]
                                    ,[CreateTime]
                                    ,[CreatorId]
                                    ,[LastModifierId]
                                    ,[LastModifyTime]
                                    FROM [Company]
                                    where Id="+Id;
            //[Name],[CreateTime],[CreatorId],[LastModifierId],[LastModifyTime],[Id]
            using (SqlConnection con = new SqlConnection(ConnectionStringCustomers))
            {
                SqlCommand sqlCommand = new SqlCommand(sql, con);
                con.Open();
                var reader=sqlCommand.ExecuteReader();
                if (reader.Read())//有資料才開始讀,有資料True
                {
                    Console.WriteLine(reader[1]);
                }
                con.Close();
                con.Dispose();
            }
            return new Company();
        }

現在只有一個表,我們還有一個表,那如果我們有很多表,那豈不是都需要寫很多方法, 如果我們希望一個方法,能回傳不同的型別,針對以上代碼優化:

        /// <summary>
        ///  如果我們希望一個方法,能回傳不同的型別?泛型+反射【不同型別,不同sql】,
        /// </summary>
        public T Find<T>(int Id)
        {
            Type type = typeof(T);

            //type.GetProperties().Select(p=> p.Name)得到屬性名稱,
            //type.GetProperties().Select(p => $"[{p.Name}]")得到它的屬性名稱并寫出如下格式:[CreatorId]*/
            //得到的是一個集合,需要把它變成一個逗號連接的字串

            //得到它的屬性名稱,把它變成一個逗號連接的字串
            string prop = string.Join(",", type.GetProperties().Select(p => $"[{p.Name}]"));
            
            //動態拼接Sql
            string sql = $"select {prop} from [{type.Name}] where ID={Id}";
            Object oObject = Activator.CreateInstance(type);
            //如果是User表User在資料庫里面屬于關鍵字,[{type.Name}]類似FROM [User]
            using (SqlConnection con = new SqlConnection(ConnectionStringCustomers))
            {
                SqlCommand sqlCommand = new SqlCommand(sql, con);
                con.Open();
                var reader = sqlCommand.ExecuteReader();
                if (reader.Read())//有資料才開始讀,有資料True
                {
                    //獲取所有的屬性進行賦值
                    foreach (var propinfo in type.GetProperties())
                    {
                        //針對資料庫名跟實力類名一一對應的場景
                        propinfo.SetValue(oObject, reader[propinfo.Name] is DBNull ? null : reader[propinfo.Name]);
                    }
                    return (T)oObject;
                }
                else
                {
                    return default;
                }
            }
            
        }

呼叫:

                {
                    SqlServerHelper sqlServer = new SqlServerHelper();
                    Company company = sqlServer.Find<Company>(1);
                    User user = sqlServer.Find<User>(1);
                }
                {
                    Console.WriteLine("---------------------------------------------Common---------------------------------------------------------");
                    People people = new People();
                    people.Id = 1;
                    people.Name = "普通";
                    people.Description = "描述:";
                    Console.WriteLine($"people.Id ={people.Id}");
                    Console.WriteLine("people.Name = {0},people.Description={1}",people.Name,people.Description);
                }
//Get:反射展示是意義的,可以不用改代碼,普通方法需要修改
//Set:感覺沒什么用,但是還是有用,
                    //反射:
                    //優點:動態,【幾乎任何一個框架里面都有反射,我們搭建框架就是希望有良好的擴展性,能夠應對將來任意的需求變化】
                    //缺點;1.使用麻煩【封裝一下】;2.避開編譯器檢查,運行時出問題;3.性能問題;
using DB.Interface;
using DB.SqlServer;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace MyReflectionFirst
{
    public class Monitor
    {
        public static void Show()
        {
            Console.WriteLine("------------------------------Monitor普通方法與反射的性能測驗--------------------------------------------");
            //初始化
            long ComonTime = 0;
            long ReflectionTime = 0;
            {
                Stopwatch stopwatch = new Stopwatch();
                stopwatch.Start();
                for (int i = 0; i < 1_000_000; i++)
                {
                    IDBHelper sqlServer = new SqlServerHelper();
                    sqlServer.Query();
                }
                stopwatch.Stop();
                ComonTime = stopwatch.ElapsedMilliseconds;
            }
            {
                Stopwatch stopwatch = new Stopwatch();
                stopwatch.Start();
                Assembly assembly = Assembly.Load("DB.SqlServer");
                Type type = assembly.GetType("DB.SqlServer.SqlServerHelper");
                for (int i = 0; i < 1_000_000; i++)
                {
                    Object oDBHelper = Activator.CreateInstance(type);
                    IDBHelper dBHelper = oDBHelper as IDBHelper;
                    dBHelper.Query();
                }
                stopwatch.Stop();
                ReflectionTime = stopwatch.ElapsedMilliseconds;
            }
            Console.WriteLine("ComonTime:{0}ms,ReflectionTime:{1}ms", ComonTime, ReflectionTime);
            //ComonTime:71ms,ReflectionTime:134ms
        }
    }
}
Monitor普通方法與反射的性能測驗

 

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/212390.html

標籤:.NET技术

上一篇:(5)ASP.NET Core3.1 Ocelot服務質量-熔斷

下一篇:C#中使用opencv處理影像

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more