欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

[C#基礎知識系列]專題十七:深入理解動態(tài)類型

本專題概要:

創(chuàng)新互聯(lián)長期為上1000+客戶提供的網(wǎng)站建設服務,團隊從業(yè)經(jīng)驗10年,關注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務;打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為南京企業(yè)提供專業(yè)的網(wǎng)站制作、網(wǎng)站建設,南京網(wǎng)站改版等技術服務。擁有十多年豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。

  • 動態(tài)類型介紹
  • 為什么需要動態(tài)類型
  • 動態(tài)類型的使用
  • 動態(tài)類型背后的故事
  • 動態(tài)類型的約束
  • 實現(xiàn)動態(tài)行為
  • 總結

引言:

終于迎來了我們C# 4中特性了,C# 4主要有兩方面的改善——Com 互操作性的改進和動態(tài)類型的引入,然而COM互操作性這里就不詳細介紹的,對于.Net 互操作性我將會在另外一個專題中詳細和大家分享下我所了解到的知識,本專題就和大家分享C# 4中的動態(tài)類型,對于動態(tài)類型,我剛聽到這個名詞的時候會有這些疑問的——動態(tài)類型到底是什么的呢? 知道動態(tài)類型大概是個什么的時候,肯定又會有這樣的疑問——C# 4中為什么要引入動態(tài)類型的?(肯定引入之后可以完成我們之前不能做的事情了,肯定是有好處的),下面就具體介紹了動態(tài)類型有哪些內容的。

一、動態(tài)類型介紹

提到動態(tài)類型當然就要說下靜態(tài)類型了,對于什么是靜態(tài)類型呢? 大家都知道之前C#一直都是靜態(tài)語言(指定的是沒有引入動態(tài)類型之前,這里說明下,不是引入了動態(tài)類型后C#就是動態(tài)語言,只是引入動態(tài)類型后,為C#語言增添了動態(tài)語言的特性,C#仍然是靜態(tài)語言),之所以稱為靜態(tài)語言,之前我們寫代碼時,例如 int i =5;這樣的代碼,此時i 我們已經(jīng)明確知道它的類型為int了,然而這樣的代碼,變量的類型的確定是在編譯時確定的,對應的,如果類型的確定是在執(zhí)行時才確定的類型,這樣的類型就是動態(tài)類型(C# 4.0中新添加了一個dynamic 關鍵字來定義我們的動態(tài)類型)。面對動態(tài)類型,C#編譯器做的工作只是完成檢查語法是否正確,但無法確定所調用的方法或屬性是否正確(之所以會這樣,主要還是因為動態(tài)類型是運行時才知道它的具體類型,所以編譯器編譯的時候肯定不知道類型,就沒辦法判斷調用的方法或屬性是不是存在和正確了,所以對于動態(tài)類型,將不能使用VS提供的智能提示的功能,這樣寫動態(tài)類型代碼時就要求開發(fā)人員對于某個動態(tài)類型必須準確知道其類型后和所具有的方法和屬性了,不能這些錯誤只能在運行程序的過程拋出異常的方式被程序員所發(fā)現(xiàn)。)

補充: 講到dynamic關鍵字,也許大家會想到C# 3中的var關鍵字,這里這里補充說明下dynamic, var區(qū)別。var 關鍵字不過是一個指令,它告訴編譯器根據(jù)變量的初始化表達式來推斷類型。(記住var并不是類型),而C# 4中引入的dynamic是類型,但是編譯時不屬于CLR類型(指的int,string,bool,double等類型,運行時肯定CLR類型中一種的),它是包含了System.Dynamic.DynamicAttribute特性的System.Object類型,但與object又不一樣,不一樣主要體現(xiàn)在動態(tài)類型不會在編譯時時執(zhí)行顯式轉換,下面給出一段代碼代碼大家就會很容易看出區(qū)別了:

  1. object obj = 10;  
  2.             Console.WriteLine(obj.GetType());  
  3.             // 使用object類型此時需要強制類型轉換,不能編譯器會出現(xiàn)編譯錯誤 
  4.             obj = (int)obj + 10;  
  5.  
  6.             dynamic dynamicnum = 10;  
  7.             Console.WriteLine(dynamicnum.GetType());  
  8.             // 對于動態(tài)類型而言,編譯時編譯器根本不知道它是什么類型, 
  9.             // 所以編譯器就判斷不了dynamicnum的類型了,所以下面的代碼不會出現(xiàn)編譯時錯誤 
  10.             // 因為dynamicnum有可能是int類型,編譯器不知道該變量的具體類型不能憑空推測類型 
  11.             // 當然也就不能提示我們編譯時錯誤了 
  12.             dynamicnum = dynamicnum + 10; 

二、為什么需要動態(tài)類型

第一部分和大家介紹了什么是動態(tài)類型,對于動態(tài)類型,總結為一句話為——運行時確定的類型。然而大家了解了動態(tài)類型到底是什么之后,當然又會出現(xiàn)新的問題了,即動態(tài)類型有什么用的呢? C# 為什么好端端的引入動態(tài)類型增加程序員的負擔呢? 事實并不是這樣的,下面就介紹了動態(tài)類型到底有什么用,它并不是所謂給程序員帶來負擔,一定程度上講是福音

2.1 使用動態(tài)類型可以減少強制類型轉換

從第一部分的補充也可以看到,使用動態(tài)類型不需要類型轉換是因為編譯器根本在編譯時的過程知道什么類型,既然不知道是什么類型,怎么判斷該類型是否能進行什么操作,所以也就不會出現(xiàn)類似“運算符“+”無法應用于“object”和“int”類型的操作數(shù)“或者”不存在int類型到某某類型的隱式轉換“的編譯時錯誤了,可能這點用戶,開發(fā)人員可能并不覺得多好的,因為動態(tài)類型沒有智能提示的功能。 但是動態(tài)類型減少了強制類型轉換的代碼之后,可讀性還是會有所增強。(這里又涉及到個人取舍問題的, 如果自己覺得那種方式方便就用那種的,沒必要一定要用動態(tài)類型,主要是看那種方式可以讓自己和其他開發(fā)人員更好理解)

2.2 使用動態(tài)類型可以使C#靜態(tài)語言中調用Python等動態(tài)語言

 對于這點,可能朋友有個疑問,為什么要在C#中使用Python這樣的動態(tài)語言呢? 對于這個疑問,就和在C#中通過P/Invoke與本地代碼交互,以及與COM互操作的道理一樣,假設我們要實現(xiàn)的功能在C#類庫中沒有,然而在Python中存在時,此時我們就可以直接調用Python中存在的功能了。

三、動態(tài)類型的使用

前面兩部分和大家介紹動態(tài)類型的一些基礎知識的,了解完基礎知識之后,大家肯定很迫不及待地想知道如何使用動態(tài)類型的,下面給出兩個例子來演示動態(tài)類型的使用的。

3.1 C# 4 通過dynamic關鍵字來實現(xiàn)動態(tài)類型

  1. dynamic dyn = 5;  
  2. Console.WriteLine(dyn.GetType());  
  3. dyn = "test string";  
  4. Console.WriteLine(dyn.GetType());  
  5. dynamic startIndex = 2;  
  6. string substring = dyn.Substring(startIndex);  
  7. Console.WriteLine(substring);  
  8. Console.Read(); 

運行結果為:
[C#基礎知識系列]專題十七:深入理解動態(tài)類型

3.2 在C#中調用Python動態(tài)語言(要運行下面的代碼,必須下載并安裝IronPython,IronPython 是在 .NET Framework 上實現(xiàn)的第一種動態(tài)語言。http://ironpython.codeplex.com下載 )

  1. // 引入動態(tài)類型之后 
  2.  // 可以在C#語言中與動態(tài)語言進行交互 
  3.  // 下面演示在C#中使用動態(tài)語言Python 
  4.      ScriptEngine engine = Python.CreateEngine();  
  5.      Console.Write("調用Python語言的print函數(shù)輸出: ");  
  6.      // 調用Python語言的print函數(shù)來輸出 
  7.      engine.Execute("print 'Hello world'");  
  8.      Console.Read(); 

運行結果:
[C#基礎知識系列]專題十七:深入理解動態(tài)類型

四、動態(tài)類型背后的故事

知道了如何在C#中調用動態(tài)語言之后,然而為什么C# 為什么可以使用動態(tài)類型呢?C#編譯器到底在背后為我們動態(tài)類型做了些什么事情的呢? 對于這些問題,答案就是DLR(Dynamic Language Runtime,動態(tài)語言運行時),DLR使得C#中可以調用動態(tài)語言以及使用dynamic的動態(tài)類型。提到DLR時,可能大家會想到.Net Framework中的CLR(公共語言運行時),然而DLR 與CLR到底是什么關系呢?下面就看看.Net 4中的組件結構圖,相信大家看完之后就會明白兩者之間的區(qū)別:

[C#基礎知識系列]專題十七:深入理解動態(tài)類型

從圖中可以看出,DLR是建立在CLR的基礎之上的,其實動態(tài)語言運行時是動態(tài)語言和C#編譯器用來動態(tài)執(zhí)行代碼的庫,它不具有JIT編譯,垃圾回收等功能。然而DLR在代碼的執(zhí)行過程中扮演的是什么樣的角色呢? DLR所扮演的角色就是——DLR通過它的綁定器(binder)和調用點(callsite),元對象來把代碼轉換為表達式樹,然后再把表達式樹編譯為IL代碼,最后由CLR編譯為本地代碼(DLR就是幫助C#編譯器來識別動態(tài)類型)。這里DLR扮演的角色并不是憑空想象出來的,而且查看它的反編譯代碼來推出來的,下面就具體給出一個例子來說明DLR背后所做的事情。C#源代碼如下:

  1. class Program  
  2.     {  
  3.         static void Main(string[] args)  
  4.         {  
  5.             dynamic text = "test text";  
  6.             int startIndex = 2;  
  7.             string substring = text.Substring(startIndex);   
  8.             Console.Read();  
  9.         }  
  10.     } 

通過Reflector工具查看生成的IL代碼如下:

  1. private static void Main(string[] args)  
  2. {  
  3.     object text = "test text";  
  4.     int startIndex = 2;  
  5.     if (<Main>o__SiteContainer0.<>p__Site1 == null)  
  6.     {  
  7.         // 創(chuàng)建用于將dynamic類型隱式轉換為字符串的調用點        <Main>o__SiteContainer0.<>p__Site1 = CallSite<Func<CallSite, object, string>>.Create(Binder.Convert(CSharpBinderFlags.None, typeof(string), typeof(Program))); 
  8.     }  
  9.     if (<Main>o__SiteContainer0.<>p__Site2 == null)  
  10.     {  
  11.         // 創(chuàng)建用于調用Substring函數(shù)的調用點        <Main>o__SiteContainer0.<>p__Site2 = CallSite<Func<CallSite, object, int, object>>.Create(Binder.InvokeMember(CSharpBinderFlags.None, "Substring", null, typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) })); 
  12.     }  
  13.         // 調用調用點,首先調用<>p_Site2,即Substring方法,再調用<>P_Site1來將結果進行轉換    string substring = <Main>o__SiteContainer0.<>p__Site1.Target(<Main>o__SiteContainer0.<>p__Site1, <Main>o__SiteContainer0.<>p__Site2.Target(<Main>o__SiteContainer0.<>p__Site2, text, startIndex)); 
  14.     Console.Read();  
  15. }  
  16.  
  17. //編譯器生成的內嵌類型為 
  18. [CompilerGenerated]  
  19. private static class <Main>o__SiteContainer0  
  20. {  
  21.     // Fields 
  22.     public static CallSite<Func<CallSite, object, string>> <>p__Site1;  
  23.     public static CallSite<Func<CallSite, object, int, object>> <>p__Site2;  

從IL代碼中可以看出Main方法內包含兩個動態(tài)操作,因為編譯器生成的內嵌類型包含兩個調用點(CallSite<T>,CallSite<T>即是System.Runtime.CompilerServices命名空間下的一個類,關于CallSite的具體信息可以查看MSDN中的介紹——CallSite<T> )字段,一個是調用Substring方法(即<>p__Site2),一個是將結果(編譯時時dynamic)動態(tài)地轉換為字符串(即<>p__Site1),下面給出動態(tài)類型的執(zhí)行過程(注意DLR中有一個緩存的概念):
[C#基礎知識系列]專題十七:深入理解動態(tài)類型

五、動態(tài)類型的約束

相信通過前面幾部分的介紹大家已經(jīng)對動態(tài)類型有了一定的了解的,尤其是第四部分的介紹之后,大家應該對于動態(tài)類型的執(zhí)行過程也有了一個清晰的認識了,然而有些函數(shù)時不能通過動態(tài)綁定來進行調用的,這里就涉及到類型類型的約束:

5.1 不能用動態(tài)類型作為參數(shù)調用擴展方法

不能用動態(tài)類型作為參數(shù)來調用擴展方法的原因是——調用點知道編譯器所知道的靜態(tài)類型,但是它不知道調用所在的源文件在哪里,以及using指令引入了哪些命名空間,所以在編譯時調用點就找不到哪些擴展方法可以使用,所以就會出現(xiàn)編譯時錯誤。下面給出一個簡單的示例程序:

  1. var numbers = Enumerable.Range(10, 10);  
  2.             dynamic number = 4;  
  3.             var error = numbers.Take(number);  // 編譯時錯誤 
  4.  
  5.             // 通過下面的方式來解決這個問題 
  6.             // 1. 將動態(tài)類型轉換為正確的類型 
  7.             var right1 = numbers.Take((int)number);  
  8.             // 2. 用調用靜態(tài)方法的方式來進行調用 
  9.             var right2 = Enumerable.Take(numbers, number); 

.2 委托與動態(tài)類型不能隱式轉換的限制

如果需要將Lambda表達式,匿名方法轉化為動態(tài)類型時,此時編譯器必須知道委托的確切類型,不能不加強制轉化就把他們設置為Delegae或object變量,此時不同string,int類型(因為前面int,string類型可以隱式轉化為動態(tài)類型,編譯器此時會把他們設置為object類型。但是匿名方法和Lambda表達式不能隱式轉化為動態(tài)類型),如果需要完成這樣的轉換,此時必須強制指定委托的類型,下面是一個演示例子:

  1. dynamic lambdarestrict = x => x + 1; // 編譯時錯誤 
  2.            // 解決方案 
  3.            dynamic rightlambda =(Func<int,int>)( x=>x+1);  
  4.  
  5.            dynamic methodrestrict = Console.WriteLine; // 編譯時錯誤 
  6.            // 解決方案 
  7.            dynamic rightmethod =(Action<string>)Console.WriteLine; 

5.3 動態(tài)類型不能調用構造函數(shù)和靜態(tài)方法的限制——即不能對動態(tài)類型調用構造函數(shù)或靜態(tài)方法,因為此時編譯器無法指定具體的類型。

  1. dynamic s = new dynamic(); 

5.4 類型聲明和泛型類型參數(shù)

不能聲明一個基類為dynamic的類型,也不能將dynamic用于類型參數(shù)的約束,或作為類型所實現(xiàn)的接口的一部分,下面看一些具體的例子來加深概念的理解:

  1. // 基類不能為dynamic 類型 
  2.     class DynamicBaseType : dynamic  
  3.     {  
  4.     }  
  5.     // dynamic類型不能為類型參數(shù)的約束 
  6.     class DynamicTypeConstrain<T> where T : dynamic  
  7.     {   
  8.     }  
  9.     // 不能作為所實現(xiàn)接口的一部分 
  10.     class DynamicInterface : IEnumerable<dynamic>  
  11.     {  
  12.     } 

六、實現(xiàn)動態(tài)的行為

介紹了這么動態(tài)類型,是不是大家都迫不及待地想知道如果讓自己的類型具有動態(tài)的行為呢? 然而實現(xiàn)動態(tài)行為有三種方式:

  • 使用ExpandObject
  • 使用DynamicObject
  • 實現(xiàn)IDynamicMetaObjectProvider接口.

下面就從最簡單的方式:

6.1 使用ExpandObject來實現(xiàn)動態(tài)的行為

  1. using System;  
  2. // 引入額外的命名空間 
  3. using System.Dynamic;  
  4.  
  5. namespace 自定義動態(tài)類型  
  6. {  
  7.     class Program  
  8.     {  
  9.         static void Main(string[] args)  
  10.         {  
  11.             dynamic expand = new ExpandoObject();  
  12.             // 動態(tài)為expand類型綁定屬性 
  13.             expand.Name = "Learning Hard";  
  14.             expand.Age = 24;  
  15.  
  16.             // 動態(tài)為expand類型綁定方法 
  17.             expand.Addmethod = (Func<int, int>)(x => x + 1);  
  18.             // 訪問expand類型的屬性和方法 
  19.             Console.WriteLine("expand類型的姓名為:"+expand.Name+" 年齡為: "+expand.Age);  
  20.             Console.WriteLine("調用expand類型的動態(tài)綁定的方法:" +expand.Addmethod(5));  
  21.             Console.Read();  
  22.         }  
  23.     }  

運行的結果和預期的一樣,運行結果為:
[C#基礎知識系列]專題十七:深入理解動態(tài)類型

6.2 使用DynamicObject來實現(xiàn)動態(tài)行為

  1. static void Main(string[] args)  
  2.         {  
  3.            dynamic dynamicobj = new DynamicType();  
  4.             dynamicobj.CallMethod();  
  5.             dynamicobj.Name = "Learning Hard";  
  6.             dynamicobj.Age = "24";  
  7.             Console.Read();  
  8.         }  
  9.   class DynamicType : DynamicObject  
  10.     {  
  11.         // 重寫方法, 
  12.         // TryXXX方法表示對對象的動態(tài)調用 
  13.         public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)  
  14.         {  
  15.             Console.WriteLine(binder.Name +" 方法正在被調用");  
  16.             result = null;  
  17.             return true;  
  18.         }  
  19.  
  20.         public override bool TrySetMember(SetMemberBinder binder, object value)  
  21.         {  
  22.             Console.WriteLine(binder.Name + " 屬性被設置," + "設置的值為: " + value);  
  23.             return true;  
  24.         }  
  25.     } 

運行結果為:
[C#基礎知識系列]專題十七:深入理解動態(tài)類型

6.3 實現(xiàn)IDynamicMetaObjectProvider接口來實現(xiàn)動態(tài)行為

由于Dynamic類型在運行時來動態(tài)創(chuàng)建對象的,所以對該類型的每個成員的訪問都會調用GetMetaObject方法來獲得動態(tài)對象,然后通過這個動態(tài)對象來進行調用,所以實現(xiàn)IDynamicMetaObjectProvider接口,需要實現(xiàn)一個GetMetaObject方法來返回DynamicMetaObject對象,演示代碼如下:

  1. static void Main(string[] args)  
  2.         {    
  3.             dynamic dynamicobj2 = new DynamicType2();  
  4.             dynamicobj2.Call();  
  5.             Console.Read();  
  6. }  
  7. public class DynamicType2 : IDynamicMetaObjectProvider  
  8.     {  
  9.         public DynamicMetaObject GetMetaObject(Expression parameter)  
  10.         {  
  11.             Console.WriteLine("開始獲得元數(shù)據(jù)......");  
  12.             return new Metadynamic(parameter,this);  
  13.         }  
  14.     }  
  15.  
  16.     // 自定義Metadynamic類 
  17.     public class Metadynamic : DynamicMetaObject  
  18.     {  
  19.         internal Metadynamic(Expression expression, DynamicType2 value)  
  20.             : base(expression, BindingRestrictions.Empty, value)  
  21.         {  
  22.         }  
  23.         // 重寫響應成員調用方法 
  24.         public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)  
  25.         {  
  26.             // 獲得真正的對象 
  27.             DynamicType2 target = (DynamicType2)base.Value;  
  28.             Expression self = Expression.Convert(base.Expression, typeof(DynamicType2));  
  29.             var restrictions = BindingRestrictions.GetInstanceRestriction(self, target);  
  30.             // 輸出綁定方法名 
  31.             Console.WriteLine(binder.Name + " 方法被調用了");  
  32.             return new DynamicMetaObject(self, restrictions);  
  33.         }  
  34.     } 

運行結果為:
[C#基礎知識系列]專題十七:深入理解動態(tài)類型

七、總結

講到這里動態(tài)類型的介紹就已經(jīng)介紹完了,本專題差不多涵蓋了動態(tài)類型中所有內容,希望通過本專題大家能夠對C# 4.0中提出來的動態(tài)類型特性可以有進一步的了解,并且本專題也是這個系列中的最后一篇文章了,到這里C#基礎知識系列也就結束了,后面我會整理出這個系列文章的一個索引,從而方便大家收藏,然而C#4中對COM互操作性也有很大的改善,關于互操作的內容將會在后面一個系列文章中和大家分享下我的學習體會。

 

當前題目:[C#基礎知識系列]專題十七:深入理解動態(tài)類型
標題URL:http://chinadenli.net/article6/jgjjig.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設計微信小程序標簽優(yōu)化網(wǎng)站內鏈App開發(fā)網(wǎng)站收錄

廣告

聲明:本網(wǎng)站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經(jīng)允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)

成都做網(wǎng)站