2013-07-29 35 views
9

我一直在寫這樣的事情在我的實現:在另一個作用域中引用時如何處理局部變量?

public void SomeMethod(int someValue, List<int> someValues) 
{ 
    Task generatedTask = null; 

    { 
    int anotherValue = 2; 
    object valuesRef = someValues; 
    generatedTask = new Task(delegate{ 
     anotherValue += someValue + GetSum(valuesRef); 
     Console.WriteLine(anotherValue); 
    }); 
    } 

    generatedTask.Start(); 
} 

不過,我不知道到底發生了什麼事在這裏...

也許一切都被「複製」到委託。或者,也許像引用類型一樣,所有值類型都會有一個與Task委託相關聯的副本,直到它存在爲止?

我只是想了解什麼剛好發生在最新的C#版本的性能問題。

+0

瞭解它的最好方法是編譯結果的調查。你可以反編譯並調查。 –

+1

Marc已經發布了反編譯代碼:) –

回答

7

優秀的問題;捕獲的變量和閉包上下文。反編譯它表明目前的編譯器創建捕獲上下文對象的位置:

public void SomeMethod(int someValue, List<int> someValues) 
{ 
    Task task; 
    <>c__DisplayClass3 class2; // <== compiler generated type; unpronounceable 
    <>c__DisplayClass1 class3; // <== compiler generated type; unpronounceable 
    class3 = new <>c__DisplayClass1(); // outer-scope context 
    class3.someValue = someValue; 
    task = null; 
    class2 = new <>c__DisplayClass3(); // <== inner-scope context 
    class2.CS$<>8__locals2 = class3; // <== bind the contexts 
    class2.anotherValue = 2; 
    class2.valuesRef = someValues; 
    task = new Task(new Action(class2.<SomeMethod>b__0)); 
    task.Start(); 
    return; 
} 

如果你的目標是儘量減少上下文對象,你可以進行手動關閉:

public void SomeMethod2(int someValue, List<int> someValues) 
{ 
    Task generatedTask = null; 
    { 
     var ctx = new MyCaptureContext(); 
     ctx.anotherValue = 2; 
     ctx.valuesRef = someValues; 
     ctx.someValue = someValue; 
     generatedTask = new Task(ctx.SomeMethod); 
    } 

    generatedTask.Start(); 
} 

class MyCaptureContext 
{ 
    // kept as fields to mimic the compiler 
    public int anotherValue; 
    public int someValue; 
    public object valuesRef; 
    public void SomeMethod() 
    { 
     anotherValue += someValue + GetSum(valuesRef); 
     Console.WriteLine(anotherValue); 
    } 
} 

您也可避免通過緩存單獨通過狀態的單個代理創建每個代理的代理創建:

public void SomeMethod(int someValue, List<int> someValues) 
{ 
    Task generatedTask = null; 
    { 
     var ctx = new MyCaptureContext(); 
     ctx.anotherValue = 2; 
     ctx.valuesRef = someValues; 
     ctx.someValue = someValue; 
     generatedTask = new Task(MyCaptureContext.SomeMethod, ctx); 
    } 

    generatedTask.Start(); 
} 
class MyCaptureContext 
{ 
    // kept as fields to mimic the compiler 
    public int anotherValue; 
    public int someValue; 
    public object valuesRef; 
    public static readonly Action<object> SomeMethod = SomeMethodImpl; 
    private static void SomeMethodImpl(object state) 
    { 
     var ctx = (MyCaptureContext)state; 
     ctx.anotherValue += ctx.someValue + GetSum(ctx.valuesRef); 
     Console.WriteLine(ctx.anotherValue); 
    } 
} 

或(cle安爾,IMO):

public void SomeMethod(int someValue, List<int> someValues) 
{ 
    Task generatedTask = null; 
    { 
     var ctx = new MyCaptureContext(); 
     ctx.anotherValue = 2; 
     ctx.valuesRef = someValues; 
     ctx.someValue = someValue; 
     generatedTask = ctx.CreateTask(); 
    } 

    generatedTask.Start(); 
} 
class MyCaptureContext 
{ 
    // kept as fields to mimic the compiler 
    public int anotherValue; 
    public int someValue; 
    public object valuesRef; 
    public Task CreateTask() 
    { 
     return new Task(someMethod, this); 
    } 
    private static readonly Action<object> someMethod = SomeMethod; 
    private static void SomeMethod(object state) 
    { 
     var ctx = (MyCaptureContext)state; 
     ctx.anotherValue += ctx.someValue + GetSum(ctx.valuesRef); 
     Console.WriteLine(ctx.anotherValue); 
    } 
} 
+0

編譯器生成這些類型只是爲了匹配每個上下文的「大小」,對吧? – cvsguimaraes

+0

@sMember定義「大小」?變量**在不同的作用域中聲明**(注意重點)獲取不同的捕獲上下文 –

+0

啊,我不知道,現在我明白了......您是否也能夠獲得生成的委託?我的意思是,'class2。 b__0'與原始代表**的** **完全相同,但是使用上下文中的值? – cvsguimaraes

1

對此的技術術語是「關閉」:綁定到聲明它的環境的函數。

函數(在這種情況下是匿名任務委託)綁定到父函數的環境,並且可以訪問其父變量,就好像它們是自己的。

更完整的解釋可以在這個優秀的blog post被發現,但這裏有一個簡單的例子:

public void SomeMethod() 
{ 
    Task generatedTask = null; 

    { 
     int someValue = 2; 

     generatedTask = new Task(delegate{ 
      Console.WriteLine(someValue); 
     }); 
    } 

    someValue = 3; 

    generatedTask.Start(); // Will write "3" to the console 
} 

幕後C#編譯器將創建一個新類來保存關閉上下文(在這個變量someValue例如),並使匿名委託成爲這個類的一個實例方法。

1

你說的是關閉。 檢查此article以瞭解封面下發生的情況。

相關問題