Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
1
Question by DonKanallie · Jul 17, 2013 at 08:34 AM · c#.netdelegate

Capturing a variable in a closure behaves differently in Unity

I have problems capturing a variable in a closure. My code looks like this:

 BetterList<Action> actions = new BetterList<Action>();
 for (int i = 0; i < 7; i++) {
     
     var innerCopy = i;
     Action action = new Action(delegate {});
     action += delegate () {
         
         Debug.Log("I am action number " + innerCopy);
     };
     actions.Add(action);
 }
 foreach (Action action in actions) {
     
     action();
 }

The last loop will output "I am action number 6" seven times.

I don't really understand why innerCopy isn't captured by the closure! Especially because this code I presented is exactly the same as an accepted answer to the same problem: http://stackoverflow.com/questions/2226510/closures-in-c-sharp-event-handler-delegates

So I am a bit confused, did I do something wrong, or is there some kind of bug in the Unity/.net environment that causes this behavior?

EDIT: Thanks to Bunny83, I tried my code in a new empty project, and it worked flawlessly. But now I discovered how to reproduce my error:

 IEnumerator ProduceError() {
     BetterList<Action> actions = new BetterList<Action>();
     for (int i = 0; i < 7; i++) {
         var innerCopy = i;
         Action action = new Action(delegate {});
         action += delegate () {
             Debug.Log("I am action number " + innerCopy);
         };
         actions.Add(action);
     }
     foreach (Action action in actions) {
         action();
     }
     yield return new WaitForEndOfFrame();
 }
 void Awake () {
     StartCoroutine(ProduceError());
 }

Somehow the coroutine screws the closures up.

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

2 Replies

· Add your reply
  • Sort: 
avatar image
6
Best Answer

Answer by Bunny83 · Jul 17, 2013 at 09:10 AM

It works perfectly for me. I get the text 7 times with a the value goes from 0 up to 6. Is there a reason why you create a multicast delegate with an empty function and your actual function? Anyways, it works as it is. Even when i put an additional debug log in your empty delegate i get all values as you would expect.

edit Note: I used the "normal" generic List, maybe that's the root of your problem...

second edit:

Well, doing this in a coroutine would be of course something totally different ;)

It's not that easy to spot but if you understand what a coroutine is it makes totally sense ;)

An iterator (or generator) function is not a usual function. The code will be packed into a class. All local variables (as well as parameters) will become member variables of this generated class. The class implements the IEnumerator interface and all your actual code goes into a statemachine like construct in the MoveNext method of that class.

That's why your closure will close around the member variable of the coroutine which will be the same each iteration.

This will work because innerCopy isn't a part of the coroutine:

 IEnumerator ProduceError()
 {
     List<Action> actions = new List<Action>();
     for (int i = 0; i < 7; i++)
     {
         System.Action doIt = ()=>{
             var innerCopy = i;
             Action action = new Action(delegate {});
             action += delegate () {
                 Debug.Log("I am action number " + innerCopy);
             };
             actions.Add(action);
         };
         doIt();
     }
     foreach (Action action in actions)
     {
         action();
     }
     yield return new WaitForEndOfFrame();
 }

I'm still a bit confused why you create a multicast delegate with an empty delegate:

     Action action = new Action(delegate {});

and then subscribe another one:

     action += delegate () {
         Debug.Log("I am action number " + innerCopy);
     };

wouldn't it be way easier this way:

 actions.Add(()=>{
     Debug.Log("I am action number " + innerCopy);
 });

Since you subscribe an anonymous delegate you can't unsubscribe it anyway. Adding more functions / delegates is always possible:

     actions[1] += ()=>{Debug.Log("ANOTHER ONE");};
Comment
Add comment · Show 5 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Bunny83 · Jul 17, 2013 at 09:13 AM 0
Share

Tried it again with the BetterList implementation that comes with NGUI and it still works the same...

Is this really your actual code?

avatar image DonKanallie · Jul 19, 2013 at 10:01 AM 0
Share

Thanks, I updated my question

avatar image Bunny83 · Jul 19, 2013 at 04:21 PM 0
Share

I've updated my answer :)

avatar image DonKanallie · Jul 22, 2013 at 09:11 AM 0
Share

Great answer! Thanks for the explanation! I usually use action = () => {}, I used the multicast delegate here only because the code was mostly copied from some example code. And since the example code was supposed to work flawlessly, I wanted to stick with it for this question.

avatar image illustir · Oct 14, 2015 at 02:36 PM 0
Share

Thanks for this answer. Googling answers to C# problems is really hard considering Unity breaks it so hard.

avatar image
0

Answer by VRtube · Feb 05, 2016 at 08:54 PM

I don't have enough permission to comment on Bunny's excellent answer, but I'd like to mention to anyone having this issue that I solved it by putting the creation and assignment of the anonymous delegate inside another (non-coroutine) function. This let's closures work as expected, at least for me.

So, to summarize:

 IEnumerator yourCoroutine(){
     //fun coroutine stuff
     setOnClickBehavior(data);
     //more coroutine stuff
 }
 
 void setOnClickBehavior(object[] params){
     //Make your delegate as usual, it will close around the params given
 }
Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

18 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Generic Triggers and Actions 0 Answers

Lifespan of a Delegate with respect to the Container 1 Answer

Delegate does not contain a definition for 'CreateDelegate' 1 Answer

`System.IO.File' does not contain a definition for `AppendText'? 1 Answer

How do I get rid of "Assembly-CSharp-firstpass" and "Assembly-CSharp-firstpass.dll" from my Assets? 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges