簡體   English   中英

為什么C#中不存在吊裝?

[英]Why doesn't hoisting exist in C#?

我每天都使用Javascript和C#,有時我不得不考慮使用Javascript時提升。 但是,C#似乎沒有實現提升(我知道),我無法弄清楚為什么。 它更像是一種設計選擇還是更類似於適用於所有靜態類型語言的安全性或語言約束?

為了記錄,我不是說我希望它存在於C#中。 我只是想明白為什么不這樣做。

編輯:當我在LINQ查詢后聲明變量時,我注意到了這個問題,但LINQ查詢被推遲到變量聲明之后。

    var results = From c In db.LoanPricingNoFee Where c.LoanTerm == LoanTerm
                   && c.LoanAdvance <= UpperLimit Select c
                   Order By c.LoanInstalment Ascending;

    Int LoanTerm = 12;

引發錯誤,而:

    int LoanTerm = 12;

    var results = From c In db.LoanPricingNoFee Where c.LoanTerm == LoanTerm
                   && c.LoanAdvance <= UpperLimit Select c
                   Order By c.LoanInstalment Ascending;

才不是。

在我使用的所有編程語言中,Javascript具有最混亂的范圍系統,並且提升是其中的一部分。 結果是在JavaScript中編寫不可預測的代碼很容易,你必須小心如何編寫它以使其成為強大而富有表現力的語言。

C#與幾乎所有其他語言一樣,假設在聲明變量之前不會使用變量。 因為它有一個編譯器,所以如果你試圖使用一個未聲明的變量,它可以通過簡單地拒絕編譯來強制執行。 在腳本語言中更常見的另一種方法是,如果在未聲明的情況下使用變量,則在首次使用時進行實例化。 這可能使得遵循代碼流程變得有些困難,並且經常被用作對行為方式的語言的批評。 大多數使用具有塊級范圍的語言(其中變量僅存在於聲明它們的級別)的人發現它是Javascript的一個特別奇怪的特性。

吊裝可能導致問題的幾個重要原因:

  • 這絕對是違反直覺的,並且使代碼更難閱讀,並且除非您意識到這種行為,否則其行為更難以預測。 難以閱讀和難以預測的代碼更有可能包含錯誤。
  • 在限制代碼中的錯誤數量方面,限制變量的生命周期可能非常有用。 如果你可以聲明變量並在兩行代碼中使用它,那么在這兩行之間有十行代碼會給意外影響變量行為帶來很多機會。 代碼完成中有很多相關信息 - 如果您還沒有閱讀,我衷心推薦它。
  • 有一個經典的UX概念的最小驚訝原則 - 像吊裝(或像Javascript處理平等的方式)的功能傾向於打破這一點。 在開發編程語言時,人們通常不會考慮用戶體驗,但實際上程序員往往是非常挑剔的用戶,當他們發現自己常常被奇怪的功能所困擾時,他們會有點脾氣暴躁。 Javascript非常幸運,它在瀏覽器中的獨特無處不在創造了一種強制普及,這意味着我們必須容忍它的許多怪癖和有問題的設計決策。

最后,我無法想象為什么它會成為像C#這樣的語言的有用補充 - 它可以帶來什么樣的好處呢?

“它更像是一種設計選擇還是更類似於適用於所有靜態類型語言的安全性或語言約束?”

這不是靜態類型的約束。 編譯器將所有變量聲明移動到作用域的頂部(在Javascript中這是函數的頂部,在C#中是當前塊的頂部)並且如果使用不同類型聲明名稱時出錯將是微不足道的。

因此,C#中不存在提升的原因純粹是一種設計決策。 為什么它的設計方式我不能說我不在團隊中。 但這可能是由於解析的簡易性(對於人類程序員和編譯器)如果變量總是在使用前聲明。

循環不變代碼運動的上下文中存在一種存在於C#(和Java)中的Hoisting形式 - 這是JIT編譯器優化,它從不影響實際的循環語句中“提升”(拉出)表達式環。

你可以在這里了解更多相關信息。

引用:

“Hoisting”是一種編譯器優化,它將循環不變代碼移出循環。 “循環不變代碼”是對循環具有引用透明性的代碼,可以用其值替換,因此它不會改變循環的語義。 此優化通過僅執行一次代碼而不是每次迭代來提高運行時性能。

所以這個編寫的代碼

public void Update(int[] arr, int x, int y)
{
    for (var i = 0; i < arr.Length; i++)
    {
        arr[i] = x + y;
    }
}

實際上是優化得有點像這樣:

public void Update(int[] arr, int x, int y)
{
    var temp = x + y;
    var length = arr.Length;
    for (var i = 0; i < length; i++)
    {
        arr[i] = temp;
    }
}

這種情況發生在JIT中 - 即在將IL轉換為本機機器指令時,因此不容易查看(您可以在這里查看這里 )。

我不是閱讀匯編的專家,但這是我使用BenchmarkDotNet運行此片段所得到的,我對它的評論表明優化實際發生了:

int[] arr = new int[10];
int x = 11;
int y = 19;

public void Update()
{
    for (var i = 0; i < arr.Length; i++)
    {
        arr[i] = x + y;
    }
}

產生:

在此輸入圖像描述

因為它是一個錯誤的概念,很可能因為急於實現JavaScript而存在。 這是一種糟糕的編碼方法,即使是經驗豐富的javascript編碼器也會誤導變量的范圍。

函數提升在編譯器必須完成的工作中可能具有不必要的成本。 例如,如果由於各種代碼控制決策返回函數而從未達到變量聲明,則處理器不需要浪費時間將未定義的空引用變量推送到堆棧內存,然后將其從堆棧中彈出作為這是方法的清理操作,甚至沒有達到。

此外,請記住JavaScript具有“可變提升”和“功能提升”(以及其他),這些處理方式不同。 函數提升在C#中沒有意義,因為它不是自上而下的解釋語言。 編譯代碼后,可能永遠不會調用該方法。 但是,在JavaScript中,當解釋器解析它們時,會立即評估“自調用”函數。

我懷疑這是一個任意的設計決定:不僅提升C#效率低下,而且對C#的工作方式也沒有意義。

暫無
暫無

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

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