您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關(guān)C#中關(guān)于反射和dynamic最佳組合是什么,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。
在 C# 中反射技術(shù)應(yīng)用廣泛,至于什么是反射.........你如果不了解的話,請看下段說明,否則請?zhí)^下段。
反射:當你背對一個美女或帥哥卻不能回頭仔細觀察研究時(純屬虛構(gòu),如有巧合、純屬雷同),一面小鏡子就能滿足你的需求。在 C# 編程過程中也經(jīng)常遇到類似的情況:有一個別人寫的 dll 類庫你想使用卻沒程序文檔資料......此時通過 C# Runtime 提供的功能,你可以把該 dll 類庫加載到你的程序中,并細細研究 dll 的每一部分內(nèi)容,這就是 C# 中的反射。
個人認為反射最突出的優(yōu)點或存在的合理性:在不修改程序原碼的情況下,實現(xiàn)程序功能的動態(tài)調(diào)整(Runtime動態(tài)對象創(chuàng)建)
示例:
interface IRun { void Run(); } class Person : IRun { public void Run() { Console.WriteLine("走,去LOL?。?quot;); } } class Car : IRun { public void Run() { Console.WriteLine("嗚..........."); } } class Program { static void Main(string[] args) { IRun e = new Person(); e.Run(); Console.ReadLine(); } }
如果將上面的Run功能并不一定是由Person來執(zhí)行,有時需要是Car有時需要Person。常見的解決方案是添加 if 等判斷結(jié)構(gòu),如下:
static void Main(string[] args) { Console.WriteLine("請輸入:Car或Person"); string type = Console.ReadLine(); IRun e = null; if ("Car" == type) { e = new Car(); }else if("Person" == type) { e = new Person(); } if(null != e) e.Run(); Console.ReadLine(); }
這種結(jié)構(gòu)確是解決了現(xiàn)在的需求,但并不健壯。隨著 IRun 接口實現(xiàn)、相關(guān)類的繼承的增加,上面的判斷結(jié)構(gòu)也會飛速增長。面向?qū)ο缶幊?、設(shè)計模式均遵循的一大原則就是封裝變換,所以上面的程序無法很好的應(yīng)對變化。在此我們并不涉及 “設(shè)計模式的” 的知識,因此下面的示例代碼只為簡化上面的程序、并未刻意套用設(shè)計模式相關(guān)知識。如下:
static void Main(string[] args) { Console.WriteLine("請輸入:Car或Person"); string type = Console.ReadLine(); string classPath = String.Format("namespace.{0}", type); IRun e = Activator.CreateInstance(null, classPath).Unwrap() as IRun; if(null != e) e.Run(); Console.ReadLine(); }
經(jīng)過上面的修改,程序可自行根據(jù)用戶的輸入,通過Activator.CreateInstance創(chuàng)建 IRun 的實例,程序此處不會再隨 IRun 的實現(xiàn)者增多這種問題的影響而發(fā)生變化。上面的這種優(yōu)點就是通過反射得到的,也是我所認為的“反射存在的合理性”。
Activator、Assembly 實現(xiàn)反射方式創(chuàng)建對象
C#中反射方式創(chuàng)建對象可以通過 Activator.CreateInstance(靜態(tài))和 Assembly.CreateInstance(非靜態(tài))來實現(xiàn),其中Assembly.CreateInstance 內(nèi)部調(diào)用的仍是Activator.CreateInstance。
根據(jù)要動態(tài)創(chuàng)建的類型對象是否處于當前程序集之中,可將反射創(chuàng)建對象分為:創(chuàng)建程序集內(nèi)的類型對象與創(chuàng)建程序集外的類型對象。
創(chuàng)建程序集內(nèi)的類型對象
private static void ReflectionIRun1(string className) { string classPath = String.Format("namespace.{0}", className); //參數(shù) null ,指出所要創(chuàng)建類型對象位于當前程序集 var handler = Activator.CreateInstance(null, classPath); IRun e = (IRun)handler.Unwrap(); Console.WriteLine(e.Run()); } private static void ReflectionIRun2(string className) { string classPath = String.Format("namespace.{0}", className); //typeof(IRun).Assembly 獲取 IRun 類型所在的程序集 object obj = typeof(IRun).Assembly.CreateInstance(null, classPath); IRun e = (IRun)obj; Console.WriteLine(e.Run()); }
創(chuàng)建程序集外的類型對象
項目中增加一個 類庫 (另一個程序集),如下圖:
添加一個 Boss 類,如下:
namespace Lib { public class Boss { private string name = "老大"; public string Name{ get {return name;} } public string Talk() { return "你們都被開除了......"; } //老板不會算賬,總是多付錢,所以很有自知之明的將Payfor設(shè)為private,防止外部人員調(diào)用 private int Payfor(int total) { return total + 10; } } }
獲取 一個 Boss 對象前,首先添加對 Lib 的引用,獲取示例如下:
private static void ReflectionBoss1() { string classPath ="Lib.Boss"; //"Lib" 參數(shù)指明要加載的程序集(即要創(chuàng)建的對象類型在哪個程序集中定義) var handler = Activator.CreateInstance("Lib", classPath); Boss b = handler.Unwrap() as Boss; Console.WriteLine(b.Talk()); } private static void ReflectionBoss2() { string classPath ="Lib.Boss"; //Assembly.Load("Lib") 加載的程序集(即要創(chuàng)建的對象類型在哪個程序集中定義) var assembly = Assembly.Load("Lib"); Boss b = (Boss)assembly.CreateInstance(classPath); Console.WriteLine(b.Talk()); }
關(guān)于反射時CLR如何查找并定位要加載的程序集,請參考MSDN中關(guān)于反射相關(guān)的知識。
反射訪問字段、調(diào)用方法(屬性)
反射除可以幫我們動態(tài)創(chuàng)建對象外,還可幫我們動態(tài)訪問對象的方法(屬性)或字段,因 C# 版本不同具體方法會有變更或擴展,更深入內(nèi)容請參考MSDN。下面僅作簡單示例(標準用法)。
給老板改名,示例:
private static void ReflectionBoss1() { string classPath = "Lib.Boss"; //"Lib" 參數(shù)指明要加載的程序集(即要創(chuàng)建的對象類型在哪個程序集中定義) var handler = Activator.CreateInstance("Lib", classPath); Boss b = handler.Unwrap() as Boss; //關(guān)鍵代碼 FieldInfo f = b.GetType().GetField("name", BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance); f.SetValue(b, "小二"); Console.WriteLine("{0}:{1}", b.Name, b.Talk()); }
輸出:
讓老板付錢:
private static void ReflectionBoss1() { string classPath = "Lib.Boss"; //"Lib" 參數(shù)指明要加載的程序集(即要創(chuàng)建的對象類型在哪個程序集中定義) var handler = Activator.CreateInstance("Lib", classPath); Boss b = handler.Unwrap() as Boss; //關(guān)鍵代碼 MethodInfo method = b.GetType().GetMethod("Payfor", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance); object money = method.Invoke(b, new object[] { 10 }); Console.WriteLine("DW039:老大給我報銷10元錢車費......"); Console.WriteLine("{0}:.....,算不清了,給你這些吧。",b.Name); Console.WriteLine("DW039:......"); Console.WriteLine("{0}:{1}", b.Name,money); Console.WriteLine("DW039:老大你真棒!"); }
輸出:
dynamic 與 反射 雙劍合璧
因為反射是運行時的類型操作,所以在編程時面臨類型不確定的問題。根據(jù)上一篇《C# 匿名對象(匿名類型)、var、動態(tài)類型 dynamic》講得 dynamic 動態(tài)類型結(jié)合我們編寫的反射程序,可以大大優(yōu)化程序邏輯(訪問受保護級別限制的代碼不在此范圍內(nèi))。
上面代碼的優(yōu)化:
private static void ReflectionBoss1() { string classPath ="Lib.Boss"; var handler = Activator.CreateInstance("Lib", classPath); dynamic b = handler.Unwrap(); Console.WriteLine(b.Talk()); } private static void ReflectionBoss2() { string classPath ="Lib.Boss"; var assembly = Assembly.Load("Lib"); dynamic b = assembly.CreateInstance(classPath); Console.WriteLine(b.Talk()); }
通過 dynamic 動態(tài)類型對象 b 來調(diào)用反射得到對象的屬性、方法可直接調(diào)用,從而省去了頻繁的類型轉(zhuǎn)換操作。
反射常見應(yīng)用場景
應(yīng)用場景我印象最深刻的是 MS Petshop 示例,從SQL Server 數(shù)據(jù)庫切換到 oracle 數(shù)據(jù)庫時反射獲得不同的數(shù)據(jù)訪問層。然我實際項目中從未遇到過中途切換數(shù)據(jù)庫的情況,其他應(yīng)用場景基本類似上面的示例。如果朋友你發(fā)現(xiàn)更多的應(yīng)用場景,請給予補充,3ks。
反射的優(yōu)缺點
優(yōu)點:反射使程序更靈活
缺點:反射運行速度相對較慢
至于反射相比普通程序慢,我沒有進行過測試也不打算進行?,F(xiàn)實情況是:Ms提倡使用 dynamic、Mvc流行、Ms對CLR不斷優(yōu)化、機器性能的提升,所以你在開發(fā)中無需過多考慮反射的性能問題。如果你寫的程序運行速度出現(xiàn)了瓶頸(應(yīng)首先確保自己程序?qū)懙暮侠恚?,研究一下?shù)據(jù)庫優(yōu)化、數(shù)據(jù)緩存、web緩存、負載均衡等技術(shù)我認為更實際一些。
看完上述內(nèi)容,你們對C#中關(guān)于反射和dynamic最佳組合是什么有進一步的了解嗎?如果還想了解更多知識或者相關(guān)內(nèi)容,請關(guān)注億速云行業(yè)資訊頻道,感謝大家的支持。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。