C# 反射與dynamic最佳組合示例代碼
在 C# 中反射技術(shù)應(yīng)用廣泛,至于什么是反射.........你如果不了解的話,請(qǐng)看下段說明,否則請(qǐng)?zhí)^下段。廣告一下:喜歡我文章的朋友請(qǐng)關(guān)注一下我的blog,這也有助于提高本人寫作的動(dòng)力。
反射:當(dāng)你背對(duì)一個(gè)美女或帥哥卻不能回頭仔細(xì)觀察研究時(shí)(純屬虛構(gòu),如有巧合、純屬雷同),一面小鏡子就能滿足你的需求。在 C# 編程過程中也經(jīng)常遇到類似的情況:有一個(gè)別人寫的 dll 類庫(kù)你想使用卻沒程序文檔資料......此時(shí)通過 C# Runtime 提供的功能,你可以把該 dll 類庫(kù)加載到你的程序中,并細(xì)細(xì)研究 dll 的每一部分內(nèi)容,這就是 C# 中的反射。
個(gè)人認(rèn)為反射最突出的優(yōu)點(diǎn)或存在的合理性:在不修改程序原碼的情況下,實(shí)現(xiàn)程序功能的動(dòng)態(tài)調(diào)整(Runtime動(dòng)態(tài)對(duì)象創(chuàng)建)
示例:
interface IRun {
void Run();
}
class Person : IRun
{
public void Run()
{
Console.WriteLine("走,去LOL啊!");
}
}
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í)行,有時(shí)需要是Car有時(shí)需要Person。常見的解決方案是添加 if 等判斷結(jié)構(gòu),如下:
static void Main(string[] args)
{
Console.WriteLine("請(qǐng)輸入: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 接口實(shí)現(xiàn)、相關(guān)類的繼承的增加,上面的判斷結(jié)構(gòu)也會(huì)飛速增長(zhǎng)。面向?qū)ο缶幊?、設(shè)計(jì)模式均遵循的一大原則就是封裝變換,所以上面的程序無法很好的應(yīng)對(duì)變化。在此我們并不涉及 “設(shè)計(jì)模式的” 的知識(shí),因此下面的示例代碼只為簡(jiǎn)化上面的程序、并未刻意套用設(shè)計(jì)模式相關(guān)知識(shí)。如下:
static void Main(string[] args)
{
Console.WriteLine("請(qǐng)輸入: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 的實(shí)例,程序此處不會(huì)再隨 IRun 的實(shí)現(xiàn)者增多這種問題的影響而發(fā)生變化。上面的這種優(yōu)點(diǎn)就是通過反射得到的,也是我所認(rèn)為的“反射存在的合理性”。
Activator、Assembly 實(shí)現(xiàn)反射方式創(chuàng)建對(duì)象
C#中反射方式創(chuàng)建對(duì)象可以通過 Activator.CreateInstance(靜態(tài))和 Assembly.CreateInstance(非靜態(tài))來實(shí)現(xiàn),其中Assembly.CreateInstance 內(nèi)部調(diào)用的仍是Activator.CreateInstance。
根據(jù)要?jiǎng)討B(tài)創(chuàng)建的類型對(duì)象是否處于當(dāng)前程序集之中,可將反射創(chuàng)建對(duì)象分為:創(chuàng)建程序集內(nèi)的類型對(duì)象與創(chuàng)建程序集外的類型對(duì)象。
創(chuàng)建程序集內(nèi)的類型對(duì)象
private static void ReflectionIRun1(string className)
{
string classPath = String.Format("namespace.{0}", className);
//參數(shù) null ,指出所要?jiǎng)?chuàng)建類型對(duì)象位于當(dā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)建程序集外的類型對(duì)象
項(xiàng)目中增加一個(gè) 類庫(kù) (另一個(gè)程序集),如下圖:
添加一個(gè) Boss 類,如下:
namespace Lib
{
public class Boss
{
private string name = "老大";
public string Name{
get {return name;}
}
public string Talk()
{
return "你們都被開除了......";
}
//老板不會(huì)算賬,總是多付錢,所以很有自知之明的將Payfor設(shè)為private,防止外部人員調(diào)用
private int Payfor(int total)
{
return total + 10;
}
}
}
獲取 一個(gè) Boss 對(duì)象前,首先添加對(duì) Lib 的引用,獲取示例如下:
private static void ReflectionBoss1()
{
string classPath ="Lib.Boss";
//"Lib" 參數(shù)指明要加載的程序集(即要?jiǎng)?chuàng)建的對(duì)象類型在哪個(gè)程序集中定義)
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") 加載的程序集(即要?jiǎng)?chuàng)建的對(duì)象類型在哪個(gè)程序集中定義)
var assembly = Assembly.Load("Lib");
Boss b = (Boss)assembly.CreateInstance(classPath);
Console.WriteLine(b.Talk());
}
關(guān)于反射時(shí)CLR如何查找并定位要加載的程序集,請(qǐng)參考MSDN中關(guān)于反射相關(guān)的知識(shí)。
反射訪問字段、調(diào)用方法(屬性)
反射除可以幫我們動(dòng)態(tài)創(chuàng)建對(duì)象外,還可幫我們動(dòng)態(tài)訪問對(duì)象的方法(屬性)或字段,因 C# 版本不同具體方法會(huì)有變更或擴(kuò)展,更深入內(nèi)容請(qǐng)參考MSDN。下面僅作簡(jiǎn)單示例(標(biāo)準(zhǔn)用法)。
給老板改名,示例:
private static void ReflectionBoss1()
{
string classPath = "Lib.Boss";
//"Lib" 參數(shù)指明要加載的程序集(即要?jiǎng)?chuàng)建的對(duì)象類型在哪個(gè)程序集中定義)
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ù)指明要加載的程序集(即要?jiǎng)?chuàng)建的對(duì)象類型在哪個(gè)程序集中定義)
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:老大給我報(bào)銷10元錢車費(fèi)......");
Console.WriteLine("{0}:.....,算不清了,給你這些吧。",b.Name);
Console.WriteLine("DW039:......");
Console.WriteLine("{0}:{1}", b.Name,money);
Console.WriteLine("DW039:老大你真棒!");
}
輸出:
dynamic 與 反射 雙劍合璧
因?yàn)榉瓷涫沁\(yùn)行時(shí)的類型操作,所以在編程時(shí)面臨類型不確定的問題。根據(jù)上一篇《C# 匿名對(duì)象(匿名類型)、var、動(dòng)態(tài)類型 dynamic》講得 dynamic 動(dòng)態(tài)類型結(jié)合我們編寫的反射程序,可以大大優(yōu)化程序邏輯(訪問受保護(hù)級(jí)別限制的代碼不在此范圍內(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 動(dòng)態(tài)類型對(duì)象 b 來調(diào)用反射得到對(duì)象的屬性、方法可直接調(diào)用,從而省去了頻繁的類型轉(zhuǎn)換操作。
反射常見應(yīng)用場(chǎng)景
應(yīng)用場(chǎng)景我印象最深刻的是 MS Petshop 示例,從SQL Server 數(shù)據(jù)庫(kù)切換到 oracle 數(shù)據(jù)庫(kù)時(shí)反射獲得不同的數(shù)據(jù)訪問層。然我實(shí)際項(xiàng)目中從未遇到過中途切換數(shù)據(jù)庫(kù)的情況,其他應(yīng)用場(chǎng)景基本類似上面的示例。如果朋友你發(fā)現(xiàn)更多的應(yīng)用場(chǎng)景,請(qǐng)給予補(bǔ)充,3ks。
反射的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):反射使程序更靈活
缺點(diǎn):反射運(yùn)行速度相對(duì)較慢
至于反射相比普通程序慢,我沒有進(jìn)行過測(cè)試也不打算進(jìn)行。現(xiàn)實(shí)情況是:Ms提倡使用 dynamic、Mvc流行、Ms對(duì)CLR不斷優(yōu)化、機(jī)器性能的提升,所以你在開發(fā)中無需過多考慮反射的性能問題。如果你寫的程序運(yùn)行速度出現(xiàn)了瓶頸(應(yīng)首先確保自己程序?qū)懙暮侠恚芯恳幌聰?shù)據(jù)庫(kù)優(yōu)化、數(shù)據(jù)緩存、web緩存、負(fù)載均衡等技術(shù)我認(rèn)為更實(shí)際一些。
上一篇:C#實(shí)現(xiàn)發(fā)送郵件的方法
欄 目:C#教程
下一篇:C#設(shè)計(jì)模式之ChainOfResponsibility職責(zé)鏈模式解決真假美猴王問題實(shí)例
本文標(biāo)題:C# 反射與dynamic最佳組合示例代碼
本文地址:http://www.jygsgssxh.com/a1/C_jiaocheng/5498.html
您可能感興趣的文章
- 01-10C#通過反射獲取當(dāng)前工程中所有窗體并打開的方法
- 01-10C#通過重寫Panel改變邊框顏色與寬度的方法
- 01-10C#實(shí)現(xiàn)實(shí)體類與字符串互相轉(zhuǎn)換的方法
- 01-10C#利用反射技術(shù)實(shí)現(xiàn)去掉按鈕選中時(shí)的邊框效果
- 01-10C#實(shí)現(xiàn)子窗體與父窗體通信方法實(shí)例總結(jié)
- 01-10時(shí)間戳與時(shí)間相互轉(zhuǎn)換(php .net精確到毫秒)
- 01-10基于C#實(shí)現(xiàn)簡(jiǎn)單離線注冊(cè)碼生成與驗(yàn)證
- 01-10C#開發(fā)中的垃圾回收機(jī)制簡(jiǎn)析
- 01-10C#編程實(shí)現(xiàn)對(duì)象與JSON串互相轉(zhuǎn)換實(shí)例分析
- 01-10C#多線程編程之使用ReaderWriterLock類實(shí)現(xiàn)多用戶讀與單用戶寫同步


閱讀排行
- 1C語言 while語句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹的示例代碼(圣誕
- 3利用C語言實(shí)現(xiàn)“百馬百擔(dān)”問題方法
- 4C語言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 01-10C#通過反射獲取當(dāng)前工程中所有窗體并
- 01-10關(guān)于ASP網(wǎng)頁(yè)無法打開的解決方案
- 01-10WinForm限制窗體不能移到屏幕外的方法
- 01-10WinForm繪制圓角的方法
- 01-10C#實(shí)現(xiàn)txt定位指定行完整實(shí)例
- 01-10WinForm實(shí)現(xiàn)仿視頻播放器左下角滾動(dòng)新
- 01-10C#停止線程的方法
- 01-10C#實(shí)現(xiàn)清空回收站的方法
- 01-10C#通過重寫Panel改變邊框顏色與寬度的
- 01-10C#實(shí)現(xiàn)讀取注冊(cè)表監(jiān)控當(dāng)前操作系統(tǒng)已
隨機(jī)閱讀
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 04-02jquery與jsp,用jquery
- 01-10C#中split用法實(shí)例總結(jié)
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 01-10delphi制作wav文件的方法
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 01-10使用C語言求解撲克牌的順子及n個(gè)骰子


