views:

336

answers:

9

I have some code,

int count = 0;

list.ForEach(i => i.SomeFunction(count++));

This seems to not increment count. Is count passed by value here? Is there any difference if I use the {} in the lambda?

int count = 0;

list.ForEach(i => 
            {
                    i.SomeFunction(count++);
            });

Update 1

Sorry, my mistake, it does update the original count.

A: 

No, there is no difference. Arguments are usually by value, unless you explicitly make it "ref" or "out" in the delegate definition used for the lambda.

Lucero
+6  A: 

count is an int, and ints are value types, which means they are indeed passed by value. There is no semantic difference between your first and second example.

(That said, it looks to me like it should be incrementing count, since it should be capturing the original reference as far as the closure. To clarify -- although count will be passed by value down into SomeFunction, things don't get "passed" into your lambda expression when you use them inside the expression -- they are the same reference as the external variable.)

mquander
+1  A: 

That should capture (close over) count as a closure.

Joel Coehoorn
A: 

Lambdas are anonymous functions, and as such, follow the same rules as passing arguments into a function.

Will Eddins
Captured variables follow very different rules...
Marc Gravell
+5  A: 

In both cases you are creating what's called a closure. Essentially, count is being wrapped in a class and that class is being used by the lambda expression.

Bill Wagner has a great book called More Effective C# and he has a blog post that describes closures in more detail.

bendewey
+3  A: 

That should increment, proof:

class Foo
{
    public void SomeFunction(int i) { }
}
static void Main()
{
    int count = 0;
    List<Foo> list = new List<Foo> {new Foo()};
    list.ForEach(i => i.SomeFunction(count++));
    Console.WriteLine(count); // 1
}

The lambda acts (as already stated) to "capture" count, essentially making the code like:

class Foo
{
    public void SomeFunction(int i) { }
}
class CaptureScope
{
    public int count;
    public void AnonMethod(Foo foo)
    {
        foo.SomeFunction(count++);
    }
}
static void Main()
{
    CaptureScope scope = new CaptureScope();
    scope.count = 0;
    List<Foo> list = new List<Foo> {new Foo()};
    list.ForEach(scope.AnonMethod);
    Console.WriteLine(scope.count); // 1
}

The above is a rough approximation of how the compiler interprets a delegate lambda. As you can see, the count is a field on a class, and is incremented.

Marc Gravell
A: 

In this case, the count variable is captured. Even if it's a struct the captured variable acts like its a reference. So, you would certainly see an incremented count after the foreach.

bruno conde
+5  A: 

The variable count is captured by the lambda expression in your situation. Any changes to count will be visible to the caller. So, for instance:

int count = 0;
list.ForEach(i => i.SomeFunction(count++));
Console.WriteLine(count);

will display the size of the list, because on each iteration you're incrementing count.

However, the call to SomeFunction passes the evaluated value of count++ (which is the value of count before the increment) by value to SomeFunction. In other words, SomeFunction can't change the value of count.

For more on closures and variable capture, see my closures article.

Jon Skeet
+3  A: 

I wanted to add a small correction here. The variable count is neither passed by value or by reference to the lambda expression because it is not a parameter. The value is instead captured by the lambda in a closure.

You should check out Raymond's series on the subject - http://blogs.msdn.com/oldnewthing/archive/2006/08/02/686456.aspx

JaredPar