Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
206 views
in Technique[技术] by (71.8m points)

C# closures behavior changes if a instantiate the object before versus inside the lambda?

So I found this weirdness where the behavior of my code changes depending on where I allocate the object that will be "closured" in my lambda function.

Here's a simplified example of what I was experiencing


Example01(){
    var myCounterLocalInstance = new MyCounter(); 
    Action<string> lambdaFunction = (args) => IncrementAndPrint(args, myCounterLocalInstance);
}

Example02(){
   Action<string> lambdaFunction = (args) => IncrementAndPrint(args, new MyCounter());
}

IncrementAndPrint(string args, MyCounter counter){
   Console.WriteLine(args + counter.GetValueAndCount());
}

class MyCounter
{
   int _counter;
   public int GetValueAndCount() => _counter++;
}

The weirdness is that Example01 and Example02 don't actually give the same results in my case. in Example01, everything works as expected, and the console output would be, if args == "cat":

  1. log: "cat0"
  2. log: "cat1"
  3. log: "cat2"

This is expected behavior, as MyCounter is stateful. Example02, however, would give the following outputs:

  1. log: "cat0"
  2. log: "cat0"
  3. log: "cat0"

The _counter member variable is reset everytime the method is called... It's like myCounter is passed by value and is being reinstantiated every time it is used in Example02, instead of being put into a closure object like in the first example -- like was expected.

Is there some explanation for this? I'm guessing this is known behavior and not a bug? There must be something about closures I don't know about. It caused me a ton of headache as I never thought behavior could change depending on where I allocate the arguments of a closure.

Thanks!

question from:https://stackoverflow.com/questions/65913691/c-sharp-closures-behavior-changes-if-a-instantiate-the-object-before-versus-insi

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You are over-thinking this.

In this method, you are creating one instance of MyCounter, that increments every time you invoke the delegate.

static void Example01(){
   var myCounterLocalInstance = new MyCounter(); 
   Action<string> lambdaFunction = (args) => IncrementAndPrint(args, myCounterLocalInstance);
   lambdaFunction("cat");
   lambdaFunction("cat");
   lambdaFunction("cat");
   lambdaFunction("cat");
}

In this method you are creating a new instance MyCounter every time you invoke the delegate, its count will always be 0!

static void Example02(){
   Action<string> lambdaFunction = (args) => IncrementAndPrint(args, new MyCounter());
   lambdaFunction("cat");
   lambdaFunction("cat");
   lambdaFunction("cat");
   lambdaFunction("cat");
}

You would get the same results if you just called the IncrementAndPrint statically:

static void Example01(){
   var myCounterLocalInstance = new MyCounter();
   IncrementAndPrint("cat",myCounterLocalInstance);
   IncrementAndPrint("cat",myCounterLocalInstance);
   IncrementAndPrint("cat",myCounterLocalInstance);
   IncrementAndPrint("cat",myCounterLocalInstance);
}

static void Example02(){
   IncrementAndPrint("cat", new MyCounter());
   IncrementAndPrint("cat", new MyCounter());
   IncrementAndPrint("cat", new MyCounter());
   IncrementAndPrint("cat", new MyCounter());
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...