自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

不同.NET方法簽名的區(qū)別詳解

開(kāi)發(fā) 后端
這里.NET中帶out、ref的方法簽名和普通方法簽名的區(qū)別,希望能對(duì)大家了解.NET方法簽名有所幫助。

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

今天有位新同事問(wèn)我.NET中帶out、ref的方法簽名和普通方法簽名的有什么區(qū)別?我覺(jué)得可以從下面的例子說(shuō)明一些關(guān)鍵的地方。

一、ref/out修飾符說(shuō)明

對(duì)于用ref/out修飾符的說(shuō)明在MSDN上有詳細(xì)的說(shuō)明,地址如下:

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

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

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查看相應(yīng)的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代碼分析

某個(gè)方法被調(diào)用時(shí)會(huì)創(chuàng)建Evaluation Stack、局部變量區(qū)、方法參數(shù)區(qū)等存儲(chǔ)區(qū)被創(chuàng)建,具體內(nèi)容可參見(jiàn)MSIL 心得一文。

IL代碼分析

1) Main()

.entrypoint,

當(dāng)前方法為入口方法;

.maxstack 1,

將創(chuàng)建的Evaluation Stack元素容量***值設(shè)置為1;

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

執(zhí)行完成后的堆棧變化

L_0002: call void ConsoleMain.Program::TestRef(int32&)     用call指令來(lái)調(diào)用方法,稍微說(shuō)明一下call指令:    Call指令只有一個(gè)參數(shù),就是被調(diào)用方法的標(biāo)記,方法的參數(shù)會(huì)從左到右壓入”方法參數(shù)區(qū)”,對(duì)于實(shí)例方法,其參數(shù)列表中的***個(gè)參數(shù)是一個(gè)類(lèi)型實(shí)例指針(this),它在調(diào)用方法的簽名中是不可見(jiàn)的但卻是***個(gè)被壓入”方法參數(shù)區(qū)”的參數(shù),怎么理解這句話呢?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方法只有一個(gè)參數(shù),那么在調(diào)用Test方法前為什么Evaluation Stack中會(huì)有兩個(gè)元素呢?實(shí)際上這個(gè)arg0就是當(dāng)前實(shí)例TestClass的this指針。對(duì)于Static方法,arg0對(duì)應(yīng)的將是其方法簽名中的***個(gè)參數(shù)。     接下來(lái)才按序?qū)⒎椒ê灻械膮?shù)壓棧。Call指令用來(lái)調(diào)用非虛方法,雖然也可以調(diào)用虛方法,但是它不會(huì)通過(guò)實(shí)例的Vrtual table來(lái)調(diào)用,因此只會(huì)調(diào)用基類(lèi)方法而不會(huì)調(diào)用子類(lèi)方法。***要說(shuō)的是編譯器可以通過(guò)方法簽名來(lái)知道當(dāng)前方法是實(shí)例方法還是靜態(tài)方法,因此不需要為此專(zhuān)門(mén)設(shè)計(jì)指令,但是通過(guò)方法簽名不能看出方法是虛的還是非虛的,所以有指令Call來(lái)調(diào)用非虛方法而由指令Callvirt來(lái)調(diào)用虛方法。

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

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

L_0008: call void ConsoleMain.Program::TestRef(int32),     Main方法的Evaluation Stack中的p的值出棧并被壓入TestRef(int32)方法的”方法參數(shù)區(qū)”,接下來(lái)執(zhí)行TestRef(int32)方法,執(zhí)行完畢后釋放相應(yīng)存儲(chǔ)區(qū)。

L_000d: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey(),     調(diào)用mscorlib.dll中定義的ReadKey方法,調(diào)用結(jié)束后返回值類(lèi)型(valuetype):System.ConsoleKeyInfo并將其壓入Main方法的Evaluation Stack。L_0012: pop  L_0013: ret,     Main方法無(wú)返回值,所以需要將其Evaluation Stack中唯一的元素出棧***返回,Main方法執(zhí)行結(jié)束,釋放相關(guān)存儲(chǔ)區(qū)。

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

L_0001: ldc.i4.2,    將int32類(lèi)型的常數(shù):2壓入Evaluation Stack;

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

L_0000:ldc.i4.1,

執(zhí)行完成后的堆棧變化情況:

堆棧變化情況

L_0001: starg.s para,

執(zhí)行完成后的堆棧變化情況:

完成后的堆棧變化情況

L_0003: ret,

方法返回,相應(yīng)的Evaluation Stack、局部變量區(qū)、方法參數(shù)區(qū)等存儲(chǔ)區(qū)被釋放。

4)、TestRef(out Int32 para)

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

在代碼中將②放開(kāi),會(huì)發(fā)現(xiàn)編譯不通過(guò),說(shuō)明方法簽名的區(qū)別如果僅僅是ref和out則無(wú)法實(shí)現(xiàn)方法的overload,也就是TestRef(ref Int32 para)和TestRef(out Int32 para)這兩個(gè)方法不能同時(shí)存在于同一個(gè)類(lèi)型中。

在代碼中將①注釋而將②放開(kāi),會(huì)發(fā)現(xiàn)編譯不通過(guò),因?yàn)椴荒軐⒁粋€(gè)未初始化的變量傳給ref修飾參數(shù)的方法,但是傳給out修飾參數(shù)的方法是可以的,但是在方法返回前一定要給out修飾的參數(shù)賦值。借用MS的一句話:

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

4、.NET方法簽名結(jié)論

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

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

(3)、使用out參數(shù)的作用:我并不關(guān)心變量的初值是什么或者我不知道初值應(yīng)該賦什么或者我只是想知道我的方法執(zhí)行完成后的狀態(tài)(例如:成功or錯(cuò)誤并給出錯(cuò)誤原因),因?yàn)榉彩怯胦ut修飾了的參數(shù)在方法中一定要為該參數(shù)重新賦值,正如MS所說(shuō):允許方法有選擇地返回值。

原文標(biāo)題:理解.Net中帶out、ref的方法簽名和普通方法簽名的區(qū)別

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

【編輯推薦】

  1. 簡(jiǎn)述C# XML解析方法的特點(diǎn)及應(yīng)用
  2. .NET對(duì)象的XML序列化和反序列化概念淺析
  3. .NET對(duì)象的XML序列化和反序列化實(shí)例詳解
  4. C# XML序列化實(shí)例淺析
  5. .NET序列化和反序列化基礎(chǔ)知識(shí)總結(jié)
責(zé)任編輯:彭凡 來(lái)源: 博客園
相關(guān)推薦

2010-04-14 09:20:26

.NET多線程

2011-05-20 16:34:35

VB.NET

2014-07-28 10:09:30

Android

2010-01-05 16:20:46

.NET Framew

2011-06-08 11:05:38

getpost

2010-07-30 08:25:20

SessionASP.NET

2009-09-10 09:50:47

ASP.NET MVC

2017-09-07 16:00:20

2010-01-21 10:48:18

VB.NET擴(kuò)展方法

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連接數(shù)據(jù)庫(kù)

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函數(shù)

2009-07-24 13:01:44

ASP.NET頁(yè)面跳轉(zhuǎn)

2012-11-27 09:34:10

.NET版本

2010-01-19 16:55:46

VB.NET聲明語(yǔ)句
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)