{"id":24,"date":"2007-05-25T13:29:27","date_gmt":"2007-05-25T17:29:27","guid":{"rendered":"http:\/\/ryepup.unwashedmeme.com\/blog\/2007\/05\/25\/eventhandlerlist-key-equality-and-auto-boxing-in-c\/"},"modified":"2007-05-25T13:59:54","modified_gmt":"2007-05-25T17:59:54","slug":"eventhandlerlist-key-equality-and-auto-boxing-in-c","status":"publish","type":"post","link":"http:\/\/ryepup.unwashedmeme.com\/blog\/2007\/05\/25\/eventhandlerlist-key-equality-and-auto-boxing-in-c\/","title":{"rendered":"EventHandlerList, key equality, and auto-boxing in C#"},"content":{"rendered":"<p>I was recently implementing some custom events, and found a couple of good (if old) articles describing how to do this efficiently using <a href=\"http:\/\/msdn2.microsoft.com\/en-us\/library\/system.componentmodel.eventhandlerlist(vs.80).aspx\">EventHandlerList<\/a>:<\/p>\n<ul>\n<li><a href=\"http:\/\/weblogs.asp.net\/rosherove\/archive\/2004\/12\/31\/344773.aspx\">Manage many events easily with EventHandlerList<\/a><\/li>\n<li><a href=\"http:\/\/weblogs.asp.net\/justin_rogers\/archive\/2004\/09\/22\/232834.aspx\">Objects with dense events, but sparse usage can benefit from custom event storage.<\/a><\/li>\n<\/ul>\n<p>Those articles go into why it&#8217;s nicer to deal with one EventHandlerList instead of many seperate EventHandlers, so read those for more information.  For the lazy, here&#8217;s some code showing how you&#8217;re supposed to use these things:<br \/>\n[csharp]<br \/>\npublic class MyClass {<br \/>\n    private EventHandlerList Events = new EventHandlerList();<\/p>\n<p>    public event EventHandler MyEvent {<br \/>\n        add { Events.AddHandler(&#8220;MyEvent&#8221;, value); }<br \/>\n        remove { Events.RemoveHandler(&#8220;MyEvent&#8221;, value); }<br \/>\n    }<\/p>\n<p>    public event EventHandler MyOtherEvent {<br \/>\n        add { Events.AddHandler(&#8220;MyOtherEvent&#8221;, value); }<br \/>\n        remove { Events.RemoveHandler(&#8220;MyOtherEvent&#8221;, value); }<br \/>\n    }<\/p>\n<p>    protected void OnMyEvent(object sender, EventArgs e) {<br \/>\n        EventHandler handler = (EventHandler) Events[&#8220;MyEvent&#8221;];<br \/>\n        if (handler != null) {<br \/>\n            handler(sender, e);<br \/>\n        }<br \/>\n    }<\/p>\n<p>    protected void OnMyOtherEvent(object sender, EventArgs e) {<br \/>\n        EventHandler handler = (EventHandler) Events[&#8220;MyOtherEvent&#8221;];<br \/>\n        if (handler != null) {<br \/>\n            handler(sender, e);<br \/>\n        }<br \/>\n    }<br \/>\n}<br \/>\n[\/csharp]<\/p>\n<p>Pretty straightforward stuff.   When you add an event handler to the list, you associate it with a key, and then when its time to trigger the events, you look for any handlers under the same key.  The other day I was putting together something similar, and ran into some unexpected behavior with the keys.  I had started by refactoring the magic strings into an enum:<br \/>\n[csharp]<br \/>\n    protected enum MyEvents {<br \/>\n        MyEvent,<br \/>\n        MyOtherEvent<br \/>\n    }<br \/>\n[\/csharp]<br \/>\nand replaced all the strings with members of that enum.  I figured this would work just fine, but the change caused my unit test to fail.  Upon debugging, the EventHandlerList was always returning null in my <code>On*Event<\/code> calls.  After some more testing, the pattern became apparent: value types don&#8217;t work as keys.  This was somewhat unexpected, as I&#8217;ve used enums like this in Hashtables all over the place before.  After doing a little <a href=\"http:\/\/www.aisto.com\/roeder\/dotnet\/\">Reflector<\/a>ing, the actual search for the key comes down to traversing a linked list with a simple equality test, something like this:<br \/>\n[csharp]<br \/>\nwhile (head != null)<br \/>\n    {<br \/>\n        if (head.key == key)<br \/>\n        {<br \/>\n            return head;<br \/>\n        }<br \/>\n        head = head.next;<br \/>\n    }<br \/>\n[\/csharp]<br \/>\nThe culprit ends up being <a href=\"http:\/\/msdn2.microsoft.com\/en-us\/library\/yz2be5wk(VS.80).aspx\">C#&#8217;s auto-boxing<\/a>.  The <code>key<\/code> is stored as an object, so my value types are being boxed on the way in, and therefore <code>==<\/code> is comparing object identity, not the object values.  If EventHandlerList used <code>head.key.Equals(key)<\/code>, everything would have worked how I expected.  The solution to rid myself of magic strings now becomes using static objects as my keys, so the object identities will match:<br \/>\n[csharp]<br \/>\nprivate static readonly object MyEventKey = new object();<br \/>\nprivate static readonly object MyOtherEventKey = new object();<br \/>\n[\/csharp]<br \/>\nThat pattern reminds me a lot of enums in Java before it got a <a href=\"http:\/\/java.sun.com\/j2se\/1.5.0\/docs\/guide\/language\/enums.html\">enum<\/a> keyword, which came on the heels of C#&#8217;s nice solution to the enumerated type problem.  It&#8217;d be nice if I could use enums for their intended purpose, but cases like this make me a bit wary.  Where else in the .NET framework am I going to find object identity equality where I expect to find object value equality?  Is there some rational explanation for this, or is this just a bug?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I was recently implementing some custom events, and found a couple of good (if old) articles describing how to do this efficiently using EventHandlerList: Manage many events easily with EventHandlerList Objects with dense events, but sparse usage can benefit from custom event storage. Those articles go into why it&#8217;s nicer to deal with one EventHandlerList [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3,2,4],"tags":[],"class_list":["post-24","post","type-post","status-publish","format-standard","hentry","category-annoying","category-c","category-code-snippet"],"_links":{"self":[{"href":"http:\/\/ryepup.unwashedmeme.com\/blog\/wp-json\/wp\/v2\/posts\/24","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/ryepup.unwashedmeme.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/ryepup.unwashedmeme.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/ryepup.unwashedmeme.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/ryepup.unwashedmeme.com\/blog\/wp-json\/wp\/v2\/comments?post=24"}],"version-history":[{"count":0,"href":"http:\/\/ryepup.unwashedmeme.com\/blog\/wp-json\/wp\/v2\/posts\/24\/revisions"}],"wp:attachment":[{"href":"http:\/\/ryepup.unwashedmeme.com\/blog\/wp-json\/wp\/v2\/media?parent=24"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/ryepup.unwashedmeme.com\/blog\/wp-json\/wp\/v2\/categories?post=24"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/ryepup.unwashedmeme.com\/blog\/wp-json\/wp\/v2\/tags?post=24"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}