簡體   English   中英

“as”關鍵字如何在內部工作?

[英]How does the “as” keyword work internally?

我知道這個關鍵字的功能,但我想知道它是如何在較低級別上工作的。

哪一個更快? 他們總能產生相同的結果嗎? 如果他們這樣做,為什么有兩種不同的方式?

// Is there an overhead? An internal try catch?
Class123 obj = someobject as Class123;

if (Class123 != null)
{
    //OK
}

要么

Class123 obj = null;

if (someobject is Class123)
{
    obj = (Class123)someobject;
}

根據MSDN:as(C#參考)

as運算符就像一個強制轉換操作。 但是,如果無法進行轉換,則返回null而不是引發異常。 請考慮以下表達式:

expression as type

除了表達式僅被計算一次之外,它等效於以下表達式。

expression is type ? (type)expression : (type)null

第一個變種( 作為操作數 )......

string str1 = strAsObject as string;
if (str1 != null)
{
    this.blabla(str1);
}

...編譯到這個IL代碼:

L_0009: ldloc.1 
L_000a: isinst string
L_000f: stloc.2 
L_0010: ldloc.2 
L_0011: ldnull 
L_0012: ceq 
L_0014: stloc.s CS$4$0000
L_0016: ldloc.s CS$4$0000
L_0018: brtrue.s L_0024
L_001a: nop 
L_001b: ldarg.0 
L_001c: ldloc.2 
L_001d: call instance void TestWinFormsApplication001.Form1::blabla(string)
L_0022: nop 
L_0023: nop 

......和第二個變體( 操作數+演員 )......

if (strAsObject is string)
{
    string str2 = (string) strAsObject;
    this.blabla(str2);
}

...編譯到這個IL代碼:

L_0024: ldloc.1 
L_0025: isinst string
L_002a: ldnull 
L_002b: cgt.un 
L_002d: ldc.i4.0 
L_002e: ceq 
L_0030: stloc.s CS$4$0000
L_0032: ldloc.s CS$4$0000
L_0034: brtrue.s L_0047
L_0036: nop 
L_0037: ldloc.1 
L_0038: castclass string
L_003d: stloc.3 
L_003e: ldarg.0 
L_003f: ldloc.3 
L_0040: call instance void TestWinFormsApplication001.Form1::blabla(string)
L_0045: nop 
L_0046: nop 

...所以你看到的唯一區別是附加castclass在線路編碼L_0038

使用as關鍵字時,沒有內部嘗試捕獲。 據我所知,該功能內置於編譯器/ CLR中,因此類型檢查是隱式和自動的。

簡單的規則
使用直接施放時,你總是希望對象有一個已知類型 (因而收到一個有用的錯誤,如果它由錯誤類型的幾率)。 當對象始終是已知類型時,請使用as關鍵字。

as關鍵字存在的原因純粹是為了程序員的方便(盡管你建議try-catch會更慢)。 你可以手動實現它,就像你指出的那樣:

var castObj = (obj is NewType) ? (NewType)obj : null;

這突出了'as'關鍵字主要用於簡潔目的的事實。

現在,兩者之間的性能差異可能微不足道。 由於類型檢查, as關鍵字可能稍微慢一些,但在絕大多數情況下,這不太可能影響代碼。 如上所述,過早優化從來都不是明智之舉。 基准如果你真的希望,但我建議只是使用哪種方法更方便/適合你的情況,而不是擔心性能(或以后,如果你絕對必須)。

直截了當地說幾件事:

當您確定對象屬於您要轉換的類型時,應該進行類型轉換。 它可以為null(在這種情況下,將返回null,除非它是您要轉換的值類型)

如果您不確定,可以使用“as”運算符。 當對象不可轉換,或者對象為null時,將返回null

“as”運算符轉換為專用的IL語句( isinst ),而類型轉換轉換為castclass IL語句,因此它構建在運行時中。 編譯器只發出正確的IL語句。

因為可能更快,因為只需要將類型ckeck一次,而+ cast需要檢查類型兩次。

這個問題已經得到了很好的回答,但到目前為止它已經缺少了難以理解的數字。

Over 100000000 iterations
AS   : Failure  00:00:00.9282403
Cast : Failure  00:00:00.9868966
AS   : Success  00:00:00.9350227
Cast : Success  00:00:01.1382759

這些數字一直以這些比例回歸

我想指出, 從這些數字中得出的唯一結論是,從績效的角度來看,通過選擇其中一種方法而獲得的結果很少 單個呼叫的差異很小(非常小的趨勢為零)。 那說,“as”更快:)

在此之后,上述數字大多是有道理的。

“作為”失敗的時間比成功時長。 成功沒有任何反應,可以按原樣使用,也可以簡單地復制。 失敗時,需要跳轉才能復制空引用。

失敗時“強制轉換”速度更快,一次調用“是”並且它不再執行任務。 成功時它會慢得多,它會對“是”和“演員”進行調整。

但是我很驚訝Cast失敗的時間比AS失敗的時間長

編輯

根據要求,在try / catch塊中投射的數字

Over 100000000 iterations
Catch : Failure 05.05:00:00 // approximately, because I didn't hang around
Catch : Success 00:00:01.4000952

產生第一組數字的代碼

class Program
{
    const int ITERATION_COUNT = 100000000;
    private static UInt64 stringCount = 0;
    private static UInt64 objectCount = 0;
    static void Main(string[] args)
    {
        Console.WriteLine("Over {0} iterations ", ITERATION_COUNT);

        string s = "Hello";
        object o = new Int32();

        RunTest("AS   : Failure  {0}", TestAs, o);
        RunTest("Cast : Failure  {0}", TestIs_And_Cast, o);
        RunTest("AS   : Success  {0}", TestAs, s);
        RunTest("Cast : Success  {0}", TestIs_And_Cast, s);

        Console.WriteLine("Press any key to stop");
        Console.ReadKey();

    }
    private static void RunTest(string testDescription, Action<object> testToRun, object arg)
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        for (int i = 0; i < ITERATION_COUNT; i++)
            testToRun(arg);
        sw.Stop();
        Console.WriteLine(testDescription, sw.Elapsed);
    }
    static void TestAs(object obj)
    {
        string s = obj as string;
        if (s != null)
            stringCount++;
        else
            objectCount++;
    }
    static void TestIs_And_Cast(object obj)
    {
        string s = null;
        if (obj is string)
        {
            s = (string)obj;
            stringCount++;
        }
        else
            objectCount++;
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM