可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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.
回答1:
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.)
回答2:
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.
回答3:
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.
回答4:
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.
回答5:
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
回答6:
That should capture (close over) count as a closure.
回答7:
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.
回答8:
Lambdas are anonymous functions, and as such, follow the same rules as passing arguments into a function.
回答9:
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
.