2011-05-07 47 views
4

我讀過,由於作用域鏈如何在javascript中工作,如果我們希望在函數F中引用未在F範圍內聲明的變量V,這對於性能而言是有利的)在F中聲明一個引用V的局部變量V2,然後通過V2訪問由V引用的對象。確實C#和VB的lambda有**範圍鏈**問題類似於JavaScript?

我想知道如果這個概念適用於C#和VB封蓋

Public Shared Function Example() 
    Dim a = 1 
    Dim b = New Object 
    Return Sub() 
       'when we use the variables a and b from here does it have to "go up the scope chain" 
      End Sub 
End Function 

(通過lambda表達式訪問函數的局部變量)順便說一句,如果答案是不過早優化是我寧願萬惡

+0

你在性能上談論問題的,還是...? – 2011-05-07 20:30:02

+0

代碼示例。另外,你是什麼意思,「這是有益的」?你指的是什麼好處? – Cheeso 2011-05-07 20:34:02

+0

@Matti Virkkunen我已經編輯@Cheeso我已經編輯了問題 – Pacerier 2011-05-07 21:11:59

回答

7

簡答:沒有。 .NET不需要遍歷範圍鏈來查找變量。

龍答:

開始用這個例子:

static Func<string> CaptureArgs(int a, int b) 
{ 
    return() => String.Format("a = {0}, b = {1}", a, b); 
} 

static void Main(string[] args) 
{ 
    Func<string> f = CaptureArgs(5, 10); 
    Console.WriteLine("f(): {0}", f()); 
    // prints f(): a = 5, b = 10 
} 

CaptureArgs方法,ab堆棧上存在。直觀地說,如果我們在一個匿名函數引用變量,返回的功能和出棧幀應該從內存中刪除ab。 (這被稱爲upward funargs problem)。因爲,在幕後,匿名函數僅僅是看中了語法糖在編譯器生成的類

C#不從向上funargs問題的困擾。上面的C#代碼變爲:

private sealed class <>c__DisplayClass1 
{ 
    // Fields 
    public int a; 
    public int b; 

    // Methods 
    public string <CaptureArgs>b__0() 
    { 
     return string.Format("a = {0}, b = {1}", this.a, this.b); 
    } 
} 

編譯器創建並返回的<>c__DisplayClass1一個新實例,從傳遞到CaptureArgs方法ab初始化其ab字段(這有效地複製來自ab堆棧到存在於堆上的字段),並將其返回給調用者。撥打f()確實是致電<>c__DisplayClass1.<CaptureArgs>b__0()

由於ab<CaptureArgs>b__0引用的是香草田,他們可以直接由委託引用,他們並不需要的範圍鏈規則的任何特殊種類。

+0

heys cool這個也和VB一樣嗎? – Pacerier 2011-05-08 00:24:58

+0

是否意味着每次創建lambda時都會生成一個新類?是不是在C#/ VB程序中廣泛使用lambdas是不鼓勵的? – Pacerier 2011-05-08 00:48:09

3

簡短的回答根:第

C#封閉在一個靜態方式實現(關閉了變量EXP合法地知道和綁定)並且不通過[[scope chain]],如在Javascript中。

運行一些測試,把你放心,那麼就不用擔心它;-)

編碼愉快。

6

如果我理解正確,JavaScript的問題如下:當您訪問(深層)嵌套作用域中的變量時,運行時需要遍歷所有父作用域以查找變量。

C#或Visual Basic中的Lambdas不會遇到此問題。

在VB或C#中,編譯器知道確切地說是您在lambda函數中引用哪個變量,因此它可以創建對該變量的直接引用。唯一的區別是捕獲的變量(從嵌套作用域訪問的變量)必須從局部變量轉換爲字段(在某個對象中也稱爲閉包)。

要添加一個例子 - 說你寫的東西(瘋了)這樣的:

Func<Func<int>> Foo() { 
    int x = 10; 
    return() => { 
    x++; 
    return() => x; 
    } 
} 

這是一個有點傻,但它證明了嵌套的作用域。該變量在一個範圍內聲明,設置在一個嵌套範圍內並在更深的範圍內閱讀。編譯器會生成如下所示的內容:

class Closure { 
    public Closure(int x) { this.x = x; } 
    public int x; 
    public Func<int> Nested1() { 
    x++; 
    return Func<int>(Nested2); 
    } 
    public int Nested2() { return x; } 
} 

Func<Func<int>> Foo() { 
    var clo = new Closure(10); 
    return Func<Func<int>>(clo.Nested1); 
} 

正如您所看到的 - 沒有經過一系列示波器。每次訪問變量x時,運行時都會直接訪問內存中的某個位置(分配在堆上而不是堆棧中)。

+0

是否意味着每次創建lambda時都會生成一個新類?是不是在C#/ VB程序中廣泛使用lambdas是不鼓勵的? – Pacerier 2011-05-08 00:27:28

+1

我相信編譯器會將一些lambdas分組到一個類中。這絕對不是避免lambda表達式的原因 - 編譯類不會爲程序大小增加太多,並且您不得不編寫更多沒有lambdas的代碼。 – 2011-05-08 03:40:17