What is the exact definition of a closure?

后端 未结 8 1122
执念已碎
执念已碎 2020-11-28 09:43

I\'ve read through previous topics on closures on stackflow and other sources and one thing is still confusing me. From what I\'ve been able to piece together technically a

8条回答
  •  日久生厌
    2020-11-28 10:04

    For the exact definition, I suggest looking at its Wikipedia entry. It's especially good. I just want to clarify it with an example.

    Assume this C# code snippet (that's supposed to perform an AND search in a list):

    List list = new List { "hello world", "goodbye world" };
    IEnumerable filteredList = list;
    var keywords = new [] { "hello", "world" };
    foreach (var keyword in keywords)
        filteredList = filteredList.Where(item => item.Contains(keyword));
    
    foreach (var s in filteredList)  // closure is called here
        Console.WriteLine(s);
    

    It's a common pitfall in C# to do something like that. If you look at the lambda expression inside Where, you'll see that it defines a function that it's behavior depends on the value of a variable at its definition site. It's like passing a variable itself to the function, rather than the value of that variable. Effectively, when this closure is called, it retrieves the value of keyword variable at that time. The result of this sample is very interesting. It prints out both "hello world" and "goodbye world", which is not what we wanted. What happened? As I said above, the function we declared with the lambda expression is a closure over keyword variable so this is what happens:

    filteredList = filteredList.Where(item => item.Contains(keyword))
                               .Where(item => item.Contains(keyword)); 
    

    and at the time of closure execution, keyword has the value "world," so we're basically filtering the list a couple times with the same keyword. The solution is:

    foreach (var keyword in keywords) {
        var temporaryVariable = keyword;
        filteredList = filteredList.Where(item => item.Contains(temporaryVariable));
    }
    

    Since temporaryVariable is scoped to the body of the foreach loop, in every iteration, it is a different variable. In effect, each closure will bind to a distinct variable (those are different instances of temporaryVariable at each iteration). This time, it'll give the correct results ("hello world"):

    filteredList = filteredList.Where(item => item.Contains(temporaryVariable_1))
                               .Where(item => item.Contains(temporaryVariable_2));
    

    in which temporaryVariable_1 has the value of "hello" and temporaryVariable_2 has the value "world" at the time of closure execution.

    Note that the closures have caused an extension to the lifetime of variables (their life were supposed to end after each iteration of the loop). This is also an important side effect of closures.

提交回复
热议问题