簡體   English   中英

C#Lambdas中類變量的范圍

[英]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.

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