Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Access to modified closure

Tags:

c#

resharper

public virtual void OnRegistrationJoin(RegistrationJoinEventArgs e)
{
    foreach (Mobile member in e.Team)
    {
        member.SendMessage(1161, "You join the {0}.", EventFullName);

        if (e.Team.Count > 1)
        {
            Joinees.Remove(member);
            member.SendMessage(1161, "Your team formation is:");

            int i = 0;

            foreach (Mobile parter in e.Team.Where(partner => partner != member).ToList())
            {
                member.SendMessage(1150, "{0}: {1}.", ++i, partner.Name);
            }
        }
    }

    Members.Add(e.Team);
}

I get "access to modified closure" warning by resharper, I was wondering what's so wrong with this code, since all I do in the inner loop is send a message?

like image 255
bevacqua Avatar asked Apr 02 '11 23:04

bevacqua


2 Answers

The problem is in:

e.Team.Where(partner => partner != member)

The variable member is a direct reference to the member variable in the outer scope. While you might not have a problem with this in the above code, it is problematic when you are running the code on multiple threads or if you aren't evaluating the lambda in the Where method right away (for example, using IQueryable instead of IEnumerable).

The reason this is a problem is that C# generates a method to then pass as a delegate to Where. That method needs direct access to member. If you were to assign the reference to another variable like this:

var m = member;
// ...
e.Team.Where(partner => partner != m);

Then C# can "capture" that value in a construct called a "closure" and pass it to the generated method. This will ensure that when member changes, the value you expect it to be when you pass it to Where isn't changed.

like image 64
codekaizen Avatar answered Oct 25 '22 00:10

codekaizen


The part resharper complains about is e.Team.Where(partner => partner != member).ToList(), as the referenced member will be changed. In this case, this isn't a problem, but in other cases, this can be a problem.

Note: you don't have to use ToList(), which forces eager evaluation of the IEnumerable<T>. Simply iterate over e.Team.Where(partner => partner != member).

like image 38
Femaref Avatar answered Oct 24 '22 23:10

Femaref