SHARE
TWEET

Events: Demystifying Common Memory Leaks

ncosentino Aug 18th, 2013 461 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // -------------------------------------------------------------------------------------
  2. // This is an example from a blog post over at Dev Leader. Check it out:
  3. // http://www.devleader.ca/2013/08/19/events-demystifying-common-memory-leaks
  4. //
  5. // Follow Dev Leader:
  6. // Facebook - http://www.facebook.com/DevLeaderCa
  7. // Google+ - https://plus.google.com/b/108985236662325804542/108985236662325804542/posts
  8. // Twitter - http://www.twitter.com/nbcosentino
  9. // -------------------------------------------------------------------------------------
  10. using System;
  11. using System.Collections.Generic;
  12. using System.Text;
  13. using System.Globalization;
  14.  
  15. namespace EventHandlerLeak
  16. {
  17.     internal class Program
  18.     {
  19.         private static void Main(string[] args)
  20.         {
  21.             while (true)
  22.             {
  23.                 Console.Clear();
  24.                 Console.WriteLine("Enter the example number to run and press enter. Press enter with no other input to exit.");
  25.                 Console.WriteLine("Example 1: Hooking with an instance-scope EventHandler");
  26.                 Console.WriteLine("Example 2: Hooking with an anonymous delegate (no parent reference)");
  27.                 Console.WriteLine("Example 3: Hooking with an anonymous delegate (with parent reference)");
  28.                 string input = Console.ReadLine();
  29.                 if (string.IsNullOrEmpty(input))
  30.                 {
  31.                     break;
  32.                 }
  33.  
  34.                 Console.Clear();
  35.  
  36.                 int example;
  37.                 if (!int.TryParse(input, NumberStyles.Integer, CultureInfo.InvariantCulture, out example))
  38.                 {
  39.                     Console.WriteLine("That wasn't an integer! Press enter.");
  40.                     Console.ReadLine();
  41.                     continue;
  42.                 }
  43.  
  44.                 switch (example)
  45.                 {
  46.                     case 1:
  47.                         Example1();
  48.                         break;
  49.                     case 2:
  50.                         Example2();
  51.                         break;
  52.                     case 3:
  53.                         Example3();
  54.                         break;
  55.                     default:
  56.                         Console.WriteLine("Example " + example + " doesn't exist! Press enter.");
  57.                         Console.ReadLine();
  58.                         continue;
  59.                 }
  60.  
  61.                 Console.WriteLine("Press enter to try another example.");
  62.                 Console.ReadLine();
  63.             }
  64.         }
  65.  
  66.         private static void Example1()
  67.         {
  68.             // first we allocate some objects. the second object we create
  69.             // depends on the first. the second object is going to hook onto
  70.             // the event exposed by the first object.
  71.             Console.WriteLine("Creating instances...");
  72.             var objectWithEvent = new ObjectWithEvent();
  73.             var objectThatHooksEvent = new ObjectThatHooksEvent(objectWithEvent);
  74.  
  75.             // we'll set the instance to null and call the GC, but since we
  76.             // have an event still hooked up to the first object, no objects
  77.             // will get finalized yet.
  78.             Console.WriteLine();
  79.             Console.WriteLine("Setting the object that hooks the event to null and calling garbage collector...");
  80.             objectThatHooksEvent = null;
  81.             GC.Collect();
  82.             Console.WriteLine("Nothing magical?");
  83.  
  84.             // let's try unhooking the event now and calling the gc. we should
  85.             // finally be able to get rid of our second object
  86.             Console.WriteLine();
  87.             Console.WriteLine("Press enter and I'll unhook the events for you and call the garbage collector again.");
  88.             Console.ReadLine();
  89.             objectWithEvent.UnhookAll();
  90.             GC.Collect();
  91.  
  92.             // now that we've ditched the second object, we can safely get rid
  93.             // of the first one!
  94.             Console.WriteLine("Neat-o, eh? Press enter and I'll set the object with the event to null and call the garbage collector one last time.");
  95.             Console.ReadLine();
  96.             objectWithEvent = null;
  97.             GC.Collect();
  98.  
  99.             Console.WriteLine("And presto! Both are gone.");
  100.         }
  101.  
  102.         private static void Example2()
  103.         {
  104.             // first we allocate some objects. the second object we create
  105.             // depends on the first. the second object is going to hook onto
  106.             // the event exposed by the first object.
  107.             Console.WriteLine("Creating instances...");
  108.             var objectWithEvent = new ObjectWithEvent();
  109.             var objectThatHooksEvent = new HookWithAnonymousDelegate(objectWithEvent);
  110.  
  111.             // we'll set the instance to null and call the GC, and since we
  112.             // our event handler we've set up doesn't have any dependencies on
  113.             // the object we've hooked with, it will get cleaned up.
  114.             Console.WriteLine();
  115.             Console.WriteLine("Setting the object that hooks the event to null and calling garbage collector...");
  116.             objectThatHooksEvent = null;
  117.             GC.Collect();
  118.  
  119.             // the first object should be just as easy to get rid of!
  120.             Console.WriteLine("Look at that! Press enter and I'll set the object with the event to null and call the garbage collector one last time.");
  121.             Console.ReadLine();
  122.             objectWithEvent = null;
  123.             GC.Collect();
  124.  
  125.             Console.WriteLine("And presto! Both are gone.");
  126.         }
  127.  
  128.         private static void Example3()
  129.         {
  130.             // first we allocate some objects. the second object we create
  131.             // depends on the first. the second object is going to hook onto
  132.             // the event exposed by the first object.
  133.             Console.WriteLine("Creating instances...");
  134.             var objectWithEvent = new ObjectWithEvent();
  135.             var objectThatHooksEvent = new HookWithAnonymousDelegate2(objectWithEvent);
  136.  
  137.             // we'll set the instance to null and call the GC, but since we
  138.             // have an event still hooked up to the first object, no objects
  139.             // will get finalized yet.
  140.             Console.WriteLine();
  141.             Console.WriteLine("Setting the object that hooks the event to null and calling garbage collector...");
  142.             objectThatHooksEvent = null;
  143.             GC.Collect();
  144.             Console.WriteLine("Nothing magical?");
  145.  
  146.             // let's try unhooking the event now and calling the gc. we should
  147.             // finally be able to get rid of our second object
  148.             Console.WriteLine();
  149.             Console.WriteLine("Press enter and I'll unhook the events for you and call the garbage collector again.");
  150.             Console.ReadLine();
  151.             objectWithEvent.UnhookAll();
  152.             GC.Collect();
  153.  
  154.             // now that we've ditched the second object, we can safely get rid
  155.             // of the first one!
  156.             Console.WriteLine("Neat-o, eh? Press enter and I'll set the object with the event to null and call the garbage collector one last time.");
  157.             Console.ReadLine();
  158.             objectWithEvent = null;
  159.             GC.Collect();
  160.  
  161.             Console.WriteLine("And presto! Both are gone.");
  162.         }
  163.  
  164.         private class ObjectWithEvent
  165.         {
  166.             ~ObjectWithEvent()
  167.             {
  168.                 Console.WriteLine(this + " is being finalized.");
  169.             }
  170.  
  171.             public event EventHandler<EventArgs> Event;
  172.  
  173.             public void UnhookAll()
  174.             {
  175.                 Event = null;
  176.             }
  177.         }
  178.  
  179.         private class ObjectThatHooksEvent
  180.         {
  181.             public ObjectThatHooksEvent(ObjectWithEvent objectWithEvent)
  182.             {
  183.                 objectWithEvent.Event += ObjectWithEvent_Event;
  184.             }
  185.  
  186.             ~ObjectThatHooksEvent()
  187.             {
  188.                 Console.WriteLine(this + " is being finalized.");
  189.             }
  190.  
  191.             private void ObjectWithEvent_Event(object sender, EventArgs e)
  192.             {
  193.                 // some fancy event
  194.             }
  195.         }
  196.  
  197.         private class HookWithAnonymousDelegate
  198.         {
  199.             public HookWithAnonymousDelegate(ObjectWithEvent objectWithEvent)
  200.             {
  201.                 objectWithEvent.Event += (sender, args) =>
  202.                 {
  203.                     // handle your event
  204.                     // (this one is special because it doesn't use anything related to the instance)
  205.                     Console.WriteLine("Event being called!");
  206.                 };
  207.             }
  208.  
  209.             ~HookWithAnonymousDelegate()
  210.             {
  211.                 Console.WriteLine(this + " is being finalized.");
  212.             }
  213.         }
  214.  
  215.         private class HookWithAnonymousDelegate2
  216.         {
  217.             public HookWithAnonymousDelegate2(ObjectWithEvent objectWithEvent)
  218.             {
  219.                 objectWithEvent.Event += (sender, args) =>
  220.                 {
  221.                     // handle your event and use something that's part of this instance
  222.                     SomeInnocentLittleMethod();
  223.                 };
  224.             }
  225.  
  226.             ~HookWithAnonymousDelegate2()
  227.             {
  228.                 Console.WriteLine(this + " is being finalized.");
  229.             }
  230.  
  231.             private void SomeInnocentLittleMethod()
  232.             {
  233.                 Console.WriteLine("... Not so innocent after all!");
  234.             }
  235.         }
  236.     }
  237. }
RAW Paste Data
Top