[英]Scope of class variables in c# lambdas
我有一些代碼等待服務器響應,並在得到它時使用lambda做事情。 它還會在此lambda中檢查類ivar _timedOut,以查看該怎么做。 我不確定的是,如果在創建lambda之后但在調用之前_timedOut在類的其他位置進行了更改,那么lambda會看到_timedOut的什么值?
我已經找到了答案,但是似乎沒有一個答案可以解決這個特定的查詢。 代碼-
public class MyClass
{
public MyClass()
{
_databaseService = //...database stuff
_uploadService = //...uploads info
_serverService = //...gets stuff from the server
_uploadService.UploadingStatusChanged += UploadStatusChanged;
}
private bool _timedOut = false;
private void GetFinalInfo()
{
FinalInfo finalInfo = _databaseService.GetFinalInfo();
if (finalInfo == null) // still have no finalInfo
{
_serverService.GetLatestFinalInfo((response, theFinalInfo) =>
{
if (!_timedOut) // this could be changed elsewhere in the class while we're waiting for the server response
{
if (response == ServerResponse.Successful)
{
_databaseService.AddFinalInfo(theFinalInfo);
// navigate to next screen
}
else
{
// do something else
}
}
});
}
else
{
// navigate to next screen
}
}
}
private void UploadStatusChanged(object s, MyEventArgs e)
{
// do stuff & call GetFinalInfo if good
}
謝謝你的幫助!
_timeout
將成為lambda閉包的一部分。
意味着lambda中的值將是被調用時的值。
我不確定的是,如果在創建lambda之后但在調用之前_timedOut在類的其他位置進行了更改,那么lambda會看到_timedOut的什么值?
lambda表達式將轉換為實例方法,因為您通過引用實例變量有效地捕獲了this
引用(並且您沒有捕獲任何局部變量)。 由λ表達式創建代表將具有的目標this
,所以在執行委托時,它會“看到”的任何變化_timedOut
。
當然,這仍然會遇到正常的線程安全問題-如果一個線程更改了變量的值,而沒有任何額外的同步或內存障礙,則另一個線程可能會嘗試讀取該變量並查看舊值。
因為沒有外部變量被認為需要捕獲, _timedOut
不會被“捕獲”。 編譯器所做的是在相關類上生成實例方法,然后將lambda中的代碼有效地“移動”到實例方法中,而不是創建閉包。 例如,編譯器將在MyClass
上生成如下方法:
[CompilerGenerated]
private void <GetFinalInfo>b__0(ServerResponse response, object theFinalInfo)
{
if (!this._timedOut)
{
if (response == ServerResponse.Successful)
{
this._databaseService.AddFinalInfo(theFinalInfo);
}
}
}
因此,lambda中的代碼將始終直接訪問_timedOut
字段(以及_databaseService
字段)。 如果你訪問的任何局部變量,則編譯器將被迫以捕獲和其他任何“外部變量”通過生成類以包含這些,在這一點this
將被捕獲。 例如,如果我們稍微更改代碼:FinalInfo finalInfo = _databaseService.GetFinalInfo(); MyStruct myStruct =新的MyStruct(); myStruct.i = 1;
if (finalInfo == null) // still have no finalInfo
{
_serverService.GetLatestFinalInfo((response, theFinalInfo) =>
{
Trace.WriteLine(myStruct.i);
if (!_timedOut) // this could be changed elsewhere in the class while we're waiting for the server response
{
if (response == ServerResponse.Successful)
{
_databaseService.AddFinalInfo(theFinalInfo);
// navigate to next screen
}
else
{
// do something else
}
}
});
}
}
編譯器將生成代碼以在GetFinalInfo中執行捕獲:
MyClass.<>c__DisplayClass2 <>c__DisplayClass = new MyClass.<>c__DisplayClass2();
<>c__DisplayClass.<>4__this = this;
FinalInfo finalInfo = this._databaseService.GetFinalInfo();
<>c__DisplayClass.bleah = default(MyStruct);
<>c__DisplayClass.bleah.i = 1;
if (finalInfo == null)
{
this._serverService.GetLatestFinalInfo(new Action<ServerResponse, object>(<>c__DisplayClass.<GetFinalInfo>b__0));
}
...顯然是this
做一個“復制”。 當然,即使在這種情況下,因為this
只能是引用,所以當引用<>c__DisplayClass.<>4__this
,它仍然直接引用原始的_timedOut
。
現在,盡管直接訪問該字段,但編譯器仍可以自由地優化其對此變量的使用,因為它不是通過易失性讀取來訪問的。 這與使用lambda無關。 如果這里有多個線程在起作用,那么您可能會遇到這樣的情況,即代碼可能看_timedOut
在非x86 / x64體系結構上對_timedOut
所有寫入。 您似乎並沒有使用多個線程,並且似乎也沒有使用_timedOut
,這會導致編譯器生成的代碼不會在x86 / x64上的其他線程上看到_timedOut
更新-但是正在更改該代碼可以介紹這一點。
外變量
任何范圍包括lambda-expression或anonymous-method-expression的局部變量,值參數或參數數組都稱為匿名函數的外部變量。 在類的實例函數成員中,此值被視為值參數,並且是該函數成員中包含的任何匿名函數的外部變量。
捕獲
當外部變量被匿名函數引用時,外部變量被認為已被匿名函數捕獲。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.