Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C#
  4. Anonymous methods problem

Anonymous methods problem

Scheduled Pinned Locked Moved C#
questionhelp
11 Posts 3 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • N Offline
    N Offline
    Narvius
    wrote on last edited by
    #1

    I have two objects. Object A holds an event. Object B assigns an anonymous delegate to Object A's event. I want to access Object A from the code in the anonymous method.

    foreach (Entity target in Entities)
    {
    [...]
    target.OnTouch += new delegate()
    {
    if (Character == '+')
    {
    Character = '/';
    passableBy = PassableFlags.normal;
    }
    return;
    };
    }

    Character and passableBy are properties of [target]. It won't compile, it says that Character and passableBy do not exist in the current context. How can I access properties of the event-holding object? [Edit] Note: This code is in Object B, and [target] point to Object A, for clarification.

    modified on Tuesday, June 30, 2009 4:19 PM

    C 1 Reply Last reply
    0
    • N Narvius

      I have two objects. Object A holds an event. Object B assigns an anonymous delegate to Object A's event. I want to access Object A from the code in the anonymous method.

      foreach (Entity target in Entities)
      {
      [...]
      target.OnTouch += new delegate()
      {
      if (Character == '+')
      {
      Character = '/';
      passableBy = PassableFlags.normal;
      }
      return;
      };
      }

      Character and passableBy are properties of [target]. It won't compile, it says that Character and passableBy do not exist in the current context. How can I access properties of the event-holding object? [Edit] Note: This code is in Object B, and [target] point to Object A, for clarification.

      modified on Tuesday, June 30, 2009 4:19 PM

      C Offline
      C Offline
      Christian Graus
      wrote on last edited by
      #2

      Where do you expect to get the scope for these properties from ? target.Character and target.passableBy will work, because the anonymous method has the scope of the spot it was created.

      Christian Graus Driven to the arms of OSX by Vista. "! i don't exactly like or do programming and it only gives me a headache." - spotted in VB forums. I can do things with my brain that I can't even google. I can flex the front part of my brain instantly anytime I want. It can be exhausting and it even causes me vision problems for some reason. - CaptainSeeSharp

      N D 2 Replies Last reply
      0
      • C Christian Graus

        Where do you expect to get the scope for these properties from ? target.Character and target.passableBy will work, because the anonymous method has the scope of the spot it was created.

        Christian Graus Driven to the arms of OSX by Vista. "! i don't exactly like or do programming and it only gives me a headache." - spotted in VB forums. I can do things with my brain that I can't even google. I can flex the front part of my brain instantly anytime I want. It can be exhausting and it even causes me vision problems for some reason. - CaptainSeeSharp

        N Offline
        N Offline
        Narvius
        wrote on last edited by
        #3

        previously, and it doesn't work. It always affects whatever [target] points to at the calling moment, ie. the wrong object. And I cannot... wait, that's an idea. Is there some way to pass over a pointer to itself together with the anonymous method? Or should I just give it up and think of something else?

        C N 2 Replies Last reply
        0
        • N Narvius

          previously, and it doesn't work. It always affects whatever [target] points to at the calling moment, ie. the wrong object. And I cannot... wait, that's an idea. Is there some way to pass over a pointer to itself together with the anonymous method? Or should I just give it up and think of something else?

          C Offline
          C Offline
          Christian Graus
          wrote on last edited by
          #4

          The method should have the scope of hte method that called it. If you created seperate variables for each object, or if you passed in a variable that was the index into the array and then looked up the object in the array in the method, that would work.

          Christian Graus Driven to the arms of OSX by Vista. "! i don't exactly like or do programming and it only gives me a headache." - spotted in VB forums. I can do things with my brain that I can't even google. I can flex the front part of my brain instantly anytime I want. It can be exhausting and it even causes me vision problems for some reason. - CaptainSeeSharp

          1 Reply Last reply
          0
          • N Narvius

            previously, and it doesn't work. It always affects whatever [target] points to at the calling moment, ie. the wrong object. And I cannot... wait, that's an idea. Is there some way to pass over a pointer to itself together with the anonymous method? Or should I just give it up and think of something else?

            N Offline
            N Offline
            Narvius
            wrote on last edited by
            #5

            That's not exactly the answer I was hoping for, but I now know how to do it. ...and will do it, after I catch some Z's. It's almost two in the morning, sheesh. Thanks!

            1 Reply Last reply
            0
            • C Christian Graus

              Where do you expect to get the scope for these properties from ? target.Character and target.passableBy will work, because the anonymous method has the scope of the spot it was created.

              Christian Graus Driven to the arms of OSX by Vista. "! i don't exactly like or do programming and it only gives me a headache." - spotted in VB forums. I can do things with my brain that I can't even google. I can flex the front part of my brain instantly anytime I want. It can be exhausting and it even causes me vision problems for some reason. - CaptainSeeSharp

              D Offline
              D Offline
              dojohansen
              wrote on last edited by
              #6

              Christian Graus wrote:

              target.Character and target.passableBy will work, because the anonymous method has the scope of the spot it was created

              How is that possible? In his code, target is just the loop variable and the anonymous method - the event handler - might run a year after the loop has finished. The handler will exist as long as delegates referring to it does, but the local variable "target" goes out of scope once the loop finishes, and it's value is changed on each loop iteration. The anonymous method is still a method and it seems to me it should have it's own scope. (I see that it makes sense for things where the anonymous method actually *executes* in the containing methods, like when using List<T>.Find(Predicate<T> match), but here we're attaching an event handler.) I've seen many of your posts and have the impression you're in the know, so it would be nice if you can explain how this works with anonymous methods as event handlers. I'm also wondering if doing it like the OP did would lead to multiple anonymous methods? It seems it would have to be so if the method shares scope with it's containing method. To return to the OP's issue, surely the normal way to do this is to use a single event handler, attach it to all the targets, and pass a target to the eventhandler as "sender" in the EventHandler delegate?

              public class Thing
              {
              public event EventHandler Touched;

              public void Touch()
              {
              //...
              if (Touched != null) Touched(this, EventArgs.Empty);
              }
              }

              public class OtherThing
              {
              List Things = App.GetThings(); // Assume we have things...
              public void Foo()
              {
              foreach (Thing t in Things)
              t.Touched += new EventHandler(thing_touched);
              }

              static void thing_touched(object sender, EventArgs e)
              {
              Thing source = (Thing)sender;
              // ... work with the Thing ...
              }
              }

              (I believe it is a good practice to make all non-virtual methods that depend only on their parameters static. It works with an instance method as well, but there is no reason for it to be an instance method.) I hope you will comment on this as I'm now confused about the scoping of anonymous methods.

              N 1 Reply Last reply
              0
              • D dojohansen

                Christian Graus wrote:

                target.Character and target.passableBy will work, because the anonymous method has the scope of the spot it was created

                How is that possible? In his code, target is just the loop variable and the anonymous method - the event handler - might run a year after the loop has finished. The handler will exist as long as delegates referring to it does, but the local variable "target" goes out of scope once the loop finishes, and it's value is changed on each loop iteration. The anonymous method is still a method and it seems to me it should have it's own scope. (I see that it makes sense for things where the anonymous method actually *executes* in the containing methods, like when using List<T>.Find(Predicate<T> match), but here we're attaching an event handler.) I've seen many of your posts and have the impression you're in the know, so it would be nice if you can explain how this works with anonymous methods as event handlers. I'm also wondering if doing it like the OP did would lead to multiple anonymous methods? It seems it would have to be so if the method shares scope with it's containing method. To return to the OP's issue, surely the normal way to do this is to use a single event handler, attach it to all the targets, and pass a target to the eventhandler as "sender" in the EventHandler delegate?

                public class Thing
                {
                public event EventHandler Touched;

                public void Touch()
                {
                //...
                if (Touched != null) Touched(this, EventArgs.Empty);
                }
                }

                public class OtherThing
                {
                List Things = App.GetThings(); // Assume we have things...
                public void Foo()
                {
                foreach (Thing t in Things)
                t.Touched += new EventHandler(thing_touched);
                }

                static void thing_touched(object sender, EventArgs e)
                {
                Thing source = (Thing)sender;
                // ... work with the Thing ...
                }
                }

                (I believe it is a good practice to make all non-virtual methods that depend only on their parameters static. It works with an instance method as well, but there is no reason for it to be an instance method.) I hope you will comment on this as I'm now confused about the scoping of anonymous methods.

                N Offline
                N Offline
                Narvius
                wrote on last edited by
                #7

                I've decided to just use EventArgs containing a reference to the appropiate object whenever it is called. Probably not very elegant, but it works, at least. It seems that anonymous methods have the scope of the creation place, but I'd have to do some research to say for certain...

                D 1 Reply Last reply
                0
                • N Narvius

                  I've decided to just use EventArgs containing a reference to the appropiate object whenever it is called. Probably not very elegant, but it works, at least. It seems that anonymous methods have the scope of the creation place, but I'd have to do some research to say for certain...

                  D Offline
                  D Offline
                  dojohansen
                  wrote on last edited by
                  #8

                  That is probably the sensible solution. But I still hope someone can help shed light on this; I've discovered something I'm unsure about and that is always an opportunity to learn. I could probably google it, but if someone has the answer at hand...

                  N 1 Reply Last reply
                  0
                  • D dojohansen

                    That is probably the sensible solution. But I still hope someone can help shed light on this; I've discovered something I'm unsure about and that is always an opportunity to learn. I could probably google it, but if someone has the answer at hand...

                    N Offline
                    N Offline
                    Narvius
                    wrote on last edited by
                    #9

                    I've checked it. It always has the scope of the place where it was created.

                    public void init()
                    {
                    int i = 30;
                    foo(delegate()
                    {
                    i = 10;
                    });
                    Console.WriteLine("i = " + i.ToString());
                    Console.ReadKey();
                    }

                    public void foo(EventHandler awesome)
                    {
                    awesome();
                    }

                    This would return "i = 10". This is somehow creepy, because it's a variable local in another method... The same happens if foo is in another class. Additionally, if the creator object doesn't exist anymore... nothing happens:

                    using System;
                    using System.Collections.Generic;
                    using System.Text;

                    namespace test2
                    {
                    class Program
                    {
                    static void Main(string[] args)
                    {
                    Thing1 thing1 = new Thing1();
                    Thing2 thing2 = thing1.Get2();
                    thing1 = null;
                    GC.Collect(); // Force Garbage Collection to be sure that thing1 doesn't exist anymore.
                    thing2.awesome(null, null);
                    return;
                    }
                    }

                    public class Thing1
                    {
                        public Thing2 Get2()
                        {
                            Thing2 thing2 = new Thing2();
                            thing2.awesome = delegate(object sender, EventArgs args)
                            {
                                thing2 = null;
                            };
                            return thing2;
                        }
                    }
                    
                    public class Thing2
                    {
                        public EventHandler awesome;
                    }
                    

                    }

                    Cleary, thing1 created the anonymous delegate. I've named the Thing2 variable the same everywhere, to be sure. Main.thing2 didn't turn into null, therefore it affected the non-existing thing1, ie. nothing happened. Also, no crash nor anything of that sort. It obviously has the scope of Thing1.Get2(), again the creator scope. [Edit] I've just noticed. The Garbage Collection doesn't take thing1! The anonymous delegate points to it after all...

                    modified on Wednesday, July 1, 2009 2:20 PM

                    D 1 Reply Last reply
                    0
                    • N Narvius

                      I've checked it. It always has the scope of the place where it was created.

                      public void init()
                      {
                      int i = 30;
                      foo(delegate()
                      {
                      i = 10;
                      });
                      Console.WriteLine("i = " + i.ToString());
                      Console.ReadKey();
                      }

                      public void foo(EventHandler awesome)
                      {
                      awesome();
                      }

                      This would return "i = 10". This is somehow creepy, because it's a variable local in another method... The same happens if foo is in another class. Additionally, if the creator object doesn't exist anymore... nothing happens:

                      using System;
                      using System.Collections.Generic;
                      using System.Text;

                      namespace test2
                      {
                      class Program
                      {
                      static void Main(string[] args)
                      {
                      Thing1 thing1 = new Thing1();
                      Thing2 thing2 = thing1.Get2();
                      thing1 = null;
                      GC.Collect(); // Force Garbage Collection to be sure that thing1 doesn't exist anymore.
                      thing2.awesome(null, null);
                      return;
                      }
                      }

                      public class Thing1
                      {
                          public Thing2 Get2()
                          {
                              Thing2 thing2 = new Thing2();
                              thing2.awesome = delegate(object sender, EventArgs args)
                              {
                                  thing2 = null;
                              };
                              return thing2;
                          }
                      }
                      
                      public class Thing2
                      {
                          public EventHandler awesome;
                      }
                      

                      }

                      Cleary, thing1 created the anonymous delegate. I've named the Thing2 variable the same everywhere, to be sure. Main.thing2 didn't turn into null, therefore it affected the non-existing thing1, ie. nothing happened. Also, no crash nor anything of that sort. It obviously has the scope of Thing1.Get2(), again the creator scope. [Edit] I've just noticed. The Garbage Collection doesn't take thing1! The anonymous delegate points to it after all...

                      modified on Wednesday, July 1, 2009 2:20 PM

                      D Offline
                      D Offline
                      dojohansen
                      wrote on last edited by
                      #10

                      Thanks for that! But here too the delegate executes before the containing function returns. It's an EventHandler delegate, but it's used as any other delegate - there's no event. Using an anonymous method for an event handler isn't necessarily weird, but accessing variables that are scoped to the containing method from the event handler is very weird indeed. I did a quick test to see how this behaved with a windows forms app. In the constructor I go into a loop to make 5 buttons, and I attach an anonymous method to the Click event. In the handler I show a messagebox displaying the value of the loop variable. I figured if the method was supposed to somehow "see" the external variable when I press a button, which I think we can agree won't happen until after the constructor has finished, then it would mean that five anonymous methods and five copies of this external variable had to be created. What actually happens though is that clicking any button shows the value 5, so presumably there is only one copy of the method. What I am now wondering is where the heck this external variable lives. Normally local variables live on the stack, but if this one did it would have died when the constructor returned. So I presume it's on the heap somewhere. I'm not sure I like this. My test code:

                      public Form1()
                      {
                      InitializeComponent();
                      for (int i = 0; i < 5; i++)
                      {
                      Button b = new Button() { Text = "button" + i, Top = 30*(i+1), Left = 50 };
                      Controls.Add(b);
                      b.Click += delegate(object sender, EventArgs e)
                      {
                      MessageBox.Show("variable i (scoped to Form1 constructor!) = " + i);
                      };
                      }
                      }

                      N 1 Reply Last reply
                      0
                      • D dojohansen

                        Thanks for that! But here too the delegate executes before the containing function returns. It's an EventHandler delegate, but it's used as any other delegate - there's no event. Using an anonymous method for an event handler isn't necessarily weird, but accessing variables that are scoped to the containing method from the event handler is very weird indeed. I did a quick test to see how this behaved with a windows forms app. In the constructor I go into a loop to make 5 buttons, and I attach an anonymous method to the Click event. In the handler I show a messagebox displaying the value of the loop variable. I figured if the method was supposed to somehow "see" the external variable when I press a button, which I think we can agree won't happen until after the constructor has finished, then it would mean that five anonymous methods and five copies of this external variable had to be created. What actually happens though is that clicking any button shows the value 5, so presumably there is only one copy of the method. What I am now wondering is where the heck this external variable lives. Normally local variables live on the stack, but if this one did it would have died when the constructor returned. So I presume it's on the heap somewhere. I'm not sure I like this. My test code:

                        public Form1()
                        {
                        InitializeComponent();
                        for (int i = 0; i < 5; i++)
                        {
                        Button b = new Button() { Text = "button" + i, Top = 30*(i+1), Left = 50 };
                        Controls.Add(b);
                        b.Click += delegate(object sender, EventArgs e)
                        {
                        MessageBox.Show("variable i (scoped to Form1 constructor!) = " + i);
                        };
                        }
                        }

                        N Offline
                        N Offline
                        Narvius
                        wrote on last edited by
                        #11

                        After these new insights, I guess I'll have to avoid relying on anonymous methods too much. This sounds like a lot of painful, difficulty-to-pin-down bugs, should something go wrong... :/

                        1 Reply Last reply
                        0
                        Reply
                        • Reply as topic
                        Log in to reply
                        • Oldest to Newest
                        • Newest to Oldest
                        • Most Votes


                        • Login

                        • Don't have an account? Register

                        • Login or register to search.
                        • First post
                          Last post
                        0
                        • Categories
                        • Recent
                        • Tags
                        • Popular
                        • World
                        • Users
                        • Groups