成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

不同.NET方法簽名的區別詳解

開發 后端
這里.NET中帶out、ref的方法簽名和普通方法簽名的區別,希望能對大家了解.NET方法簽名有所幫助。

我們將要講述的是.NET方法簽名的不同種類,希望這些差異的對比,能對大家了解.NET方法簽名有所幫助。

今天有位新同事問我.NET中帶out、ref的方法簽名和普通方法簽名的有什么區別?我覺得可以從下面的例子說明一些關鍵的地方。

一、ref/out修飾符說明

對于用ref/out修飾符的說明在MSDN上有詳細的說明,地址如下:

http://msdn.microsoft.com/en-us/library/t3c3bfhx(VS.80).aspx

二、透過IL代碼觀察ref/out修飾的方法簽名(以值類型為例)

1、示例代碼:

  1. using System;  
  2.  
  3. namespace ConsoleMain  
  4. {  
  5.     class Program  
  6.     {  
  7.         static void Main()  
  8.         {  
  9.             Int32 p ;  
  10.  
  11.             TestRef(out p);                             //①  
  12.  
  13.             //TestRef(ref p)                        //②  
  14.               
  15.             TestRef(p);                         //③  
  16.  
  17.             Console.ReadKey();  
  18.         }  
  19.  
  20.         static void TestRef(Int32 para)                 //④  
  21.         {  
  22.             para = 1;  
  23.         }  
  24.  
  25.         static void TestRef(out Int32 para)                //⑤  
  26.         {  
  27.             para = 2;  
  28.         }  
  29.  
  30.         /*static void TestRef(ref Int32 para)                //⑥  
  31.         {  
  32.             Para3 =  3;  
  33.         } */ 
  34.     }  

2、使用Reflector查看相應的IL代碼如下:

(1) Main()

  1. .method private hidebysig static void Main() cil managed  
  2. {  
  3.     .entrypoint  
  4.     .maxstack 1  
  5.     .locals init (  
  6.         [0] int32 p)  
  7.     L_0000: ldloca.s p  
  8.     L_0002: call void ConsoleMain.Program::TestRef(int32&)  
  9.     L_0007: ldloc.0   
  10.     L_0008: call void ConsoleMain.Program::TestRef(int32)  
  11.     L_000d: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()  
  12.     L_0012: pop   
  13.     L_0013: ret   

(2) TestRef(ref Int32 para)

  1. .method private hidebysig static void TestRef(int32& para) cil managed  
  2. {  
  3.     .maxstack 8  
  4.     L_0000: ldarg.0   
  5.     L_0001: ldc.i4.2   
  6.     L_0002: stind.i4   
  7.     L_0003: ret   

(3) TestRef(Int32 para)

  1. .method private hidebysig static void TestRef(int32 para) cil managed  
  2. {  
  3.     .maxstack 8  
  4.     L_0000: ldc.i4.1   
  5.     L_0001: starg.s para  
  6.     L_0003: ret   

(4) TestRef(out Int32 para)

  1. .method private hidebysig static void TestRef([out] int32& para) cil managed  
  2. {  
  3.     .maxstack 8  
  4.     L_0000: ldarg.0   
  5.     L_0001: ldc.i4.2   
  6.     L_0002: stind.i4   
  7.     L_0003: ret   

3、IL代碼分析

某個方法被調用時會創建Evaluation Stack、局部變量區、方法參數區等存儲區被創建,具體內容可參見MSIL 心得一文。

IL代碼分析

1) Main()

.entrypoint,

當前方法為入口方法;

.maxstack 1,

將創建的Evaluation Stack元素容量***值設置為1;

.locals init ([0] int32 p),     建立方法的“局部變量區”,該區包含一個叫p的類型為int32的局部變量;L_0000: ldloca.s p,從“局部變量區”取得局部變量p的內存地址并對Evaluation Stack壓棧,執行完成后的堆棧變化情況:

執行完成后的堆棧變化

L_0002: call void ConsoleMain.Program::TestRef(int32&)     用call指令來調用方法,稍微說明一下call指令:    Call指令只有一個參數,就是被調用方法的標記,方法的參數會從左到右壓入”方法參數區”,對于實例方法,其參數列表中的***個參數是一個類型實例指針(this),它在調用方法的簽名中是不可見的但卻是***個被壓入”方法參數區”的參數,怎么理解這句話呢?public class TestClass{    private void InvokeTest()    {        Test(1);    }    private void Test(Int32 i)    {               }       }   InvokeTest方法的IL代碼如下:
.method private hidebysig instance void InvokeTest() cil managed
{
   .maxstack 8
    L_0000: ldarg.0
    L_0001: ldc.i4.1
    L_0002: call instance void ConsoleMain.TestClass::Test(int32)
    L_0007: ret
}

從這里可以看到,有代碼L_0000: ldarg.0,Test方法只有一個參數,那么在調用Test方法前為什么Evaluation Stack中會有兩個元素呢?實際上這個arg0就是當前實例TestClass的this指針。對于Static方法,arg0對應的將是其方法簽名中的***個參數。     接下來才按序將方法簽名中的參數壓棧。Call指令用來調用非虛方法,雖然也可以調用虛方法,但是它不會通過實例的Vrtual table來調用,因此只會調用基類方法而不會調用子類方法。***要說的是編譯器可以通過方法簽名來知道當前方法是實例方法還是靜態方法,因此不需要為此專門設計指令,但是通過方法簽名不能看出方法是虛的還是非虛的,所以有指令Call來調用非虛方法而由指令Callvirt來調用虛方法。

回到主題:     Main方法的Evaluation Stack中的&p出棧并被壓入TestRef(int32&)方法的”方法參數區”,接下來執行TestRef(int32&)方法,由于方法無返回值,所以執行完成后Main方法的Evaluation Stack為空;從這里也可以看出ref被編譯器編譯為&,很熟悉吧,呵呵。

L_0007: ldloc.0,     將Main方法局部變量p的值壓棧。

L_0008: call void ConsoleMain.Program::TestRef(int32),     Main方法的Evaluation Stack中的p的值出棧并被壓入TestRef(int32)方法的”方法參數區”,接下來執行TestRef(int32)方法,執行完畢后釋放相應存儲區。

L_000d: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey(),     調用mscorlib.dll中定義的ReadKey方法,調用結束后返回值類型(valuetype):System.ConsoleKeyInfo并將其壓入Main方法的Evaluation Stack。L_0012: pop  L_0013: ret,     Main方法無返回值,所以需要將其Evaluation Stack中唯一的元素出棧***返回,Main方法執行結束,釋放相關存儲區。

2)、TestRef(ref Int32 para) L_0000: ldarg.0,    方法參數區中第0個變量的值出棧后壓入Evaluation Stack;

L_0001: ldc.i4.2,    將int32類型的常數:2壓入Evaluation Stack;

L_0002: stind.i4,    將Evaluation Stack中的兩個元素彈出(***個元素為一個值value,第二個元素應該是某個變量的內存地址address),***將value存儲到address所指向的內存空間中。L_0003: ret,方法返回,釋放相應存儲區域。 3)、TestRef(Int32 para)

L_0000:ldc.i4.1,

執行完成后的堆棧變化情況:

堆棧變化情況

L_0001: starg.s para,

執行完成后的堆棧變化情況:

完成后的堆棧變化情況

L_0003: ret,

方法返回,相應的Evaluation Stack、局部變量區、方法參數區等存儲區被釋放。

4)、TestRef(out Int32 para)

從方法簽名上看它只比TestRef(ref Int32 para)多一個[out],其它內容完全一樣。

在代碼中將②放開,會發現編譯不通過,說明方法簽名的區別如果僅僅是ref和out則無法實現方法的overload,也就是TestRef(ref Int32 para)和TestRef(out Int32 para)這兩個方法不能同時存在于同一個類型中。

在代碼中將①注釋而將②放開,會發現編譯不通過,因為不能將一個未初始化的變量傳給ref修飾參數的方法,但是傳給out修飾參數的方法是可以的,但是在方法返回前一定要給out修飾的參數賦值。借用MS的一句話:

the ref and out keywords are treated differently at run-time, but they are treated the same at compile time.

4、.NET方法簽名結論

(1)、有ref和out修飾參數的方法和普通方法在調用前的數據準備是不一樣的,由L_0000: ldloca.s p和L_0007: ldloc.0可以看到,前者是獲取目標變量的內存地址,后者是獲取目標變量的值,這就是所謂的傳引用和傳值。

(2)、兩個方法的區別僅僅是相同參數,一個使用的修飾符是ref,另一個是out,那么無法重載這兩個方法,且分別編譯它們得到的IL代碼完全一樣,只是方法簽名中由out修飾的那個參數前會有個token[out]。

(3)、使用out參數的作用:我并不關心變量的初值是什么或者我不知道初值應該賦什么或者我只是想知道我的方法執行完成后的狀態(例如:成功or錯誤并給出錯誤原因),因為凡是用out修飾了的參數在方法中一定要為該參數重新賦值,正如MS所說:允許方法有選擇地返回值。

原文標題:理解.Net中帶out、ref的方法簽名和普通方法簽名的區別

鏈接:http://www.cnblogs.com/vivounicorn/archive/2009/09/17/1568242.html

【編輯推薦】

  1. 簡述C# XML解析方法的特點及應用
  2. .NET對象的XML序列化和反序列化概念淺析
  3. .NET對象的XML序列化和反序列化實例詳解
  4. C# XML序列化實例淺析
  5. .NET序列化和反序列化基礎知識總結
責任編輯:彭凡 來源: 博客園
相關推薦

2010-04-14 09:20:26

.NET多線程

2011-05-20 16:34:35

VB.NET

2010-01-05 16:20:46

.NET Framew

2014-07-28 10:09:30

Android

2011-06-08 11:05:38

getpost

2009-09-10 09:50:47

ASP.NET MVC

2010-07-30 08:25:20

SessionASP.NET

2010-01-21 10:48:18

VB.NET擴展方法

2017-09-07 16:00:20

2010-10-08 21:14:08

2011-05-06 10:12:01

線材

2009-10-14 14:50:16

VB6.0VB.NET

2009-08-25 16:01:32

C#.NET連接數據庫

2011-07-04 14:29:25

Qt Designer 容器

2010-01-05 15:43:13

.NET Framew

2009-11-02 14:35:52

VB.NET打包

2023-11-29 07:47:29

Golang函數

2009-07-24 13:01:44

ASP.NET頁面跳轉

2012-11-27 09:34:10

.NET版本

2010-01-19 16:55:46

VB.NET聲明語句
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日韩黄色av| 日本不卡视频在线播放 | 亚洲午夜在线 | 国产精品中文字幕在线 | 伊人看片| www.男人天堂.com | 亚洲国产精品suv | 成人精品高清 | 国产精品久久久久久亚洲调教 | 精品久久久久久亚洲综合网 | 亚洲成人网在线播放 | 亚洲天堂中文字幕 | 日日精品| 中文字幕成人av | 伊人狠狠 | 欧美午夜一区二区三区免费大片 | 免费在线观看av网址 | 日本久久久久久久久 | 亚洲国产成人av好男人在线观看 | 亚洲精品一区二区三区丝袜 | 福利片一区二区 | 亚洲精品久久久久久久久久久久久 | 精品一区二区三区在线播放 | 在线观看黄免费 | h视频在线免费 | 99久久影院 | 久草在线| 午夜91 | 在线视频中文字幕 | 蜜月aⅴ免费一区二区三区 99re在线视频 | 久久99蜜桃综合影院免费观看 | 精品久久99 | 日韩免费一二三区 | 中文字幕日韩一区 | 一区二区高清在线观看 | 精品美女在线观看视频在线观看 | 91大神新作在线观看 | 蜜桃在线视频 | 波霸ol一区二区 | 日本成人在线观看网站 | 国产主播第一页 |