{"id":1793,"date":"2009-05-18T16:34:00","date_gmt":"2009-05-18T16:34:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/dotnet\/2009\/05\/18\/the-conditional-weak-table-enabling-dynamic-object-properties\/"},"modified":"2021-10-04T15:43:59","modified_gmt":"2021-10-04T22:43:59","slug":"the-conditional-weak-table-enabling-dynamic-object-properties","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/the-conditional-weak-table-enabling-dynamic-object-properties\/","title":{"rendered":"The Conditional Weak Table: Enabling Dynamic Object Properties"},"content":{"rendered":"<p class=\"MsoNormal\"><span lang=\"EN-GB\"><font size=\"3\" face=\"Calibri\">The Dynamic Language Runtime allows you to implement a dynamic language (such as Ruby or Python) on top of the CLR. There are a lot of challenges to making everything work right. One particularly difficult aspect was enabling Ruby to attach arbitrary &#8220;properties&#8221; to instanced .NET managed objects at runtime. <\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN-GB\"><font size=\"3\" face=\"Calibri\">If a Ruby developer sets an instance variable on a Ruby object everything can be handled by the Ruby compiler. But the Dynamic Language Runtime (DLR) offers the ability to use .NET types in your dynamic language. When a Ruby developer sets instance variables on .NET objects the objects are stored as keys in an internal table and the instance variables are stored as values.<\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN-GB\"><font size=\"3\" face=\"Calibri\">For the first release, the DLR used a weak hashtable implementation to store keys and values. But&nbsp;in CLR v4, we did an interesting feature for the benefit of language compiler authors: the <strong>Conditional Weak Table<\/strong>. You can find the class <\/font><\/span><span lang=\"EN-GB\">ConditionalWeakTable&lt;TKey, TValue&gt;<\/span><font size=\"3\"><font face=\"Calibri\"><span lang=\"EN-GB\"> in the System.Runtime. CompilerServices namespace. It&rsquo;s in CompilerServices because it&rsquo;s not a general-purpose dictionary type: we intend for it to only be used by compiler writers.<\/span><span lang=\"EN-GB\"><\/p>\n<p><\/span><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN-GB\"><font size=\"3\" face=\"Calibri\">The reason we needed to expose a new class in the runtime is that in some scenarios the keys and values can be pointing at each other. This causes problems in a weak hashtable because the GC can never free memory that is still referenced. Consider the following Ruby code, which sets instance variables (x and y) on a .NET object<\/font><\/span><\/p>\n<p class=\"MsoListParagraphCxSpFirst\"><span># Allow Ruby to access .NET types<\/p>\n<p><\/span><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span>require &#8216;mscorlib&#8217;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span>module System<\/p>\n<p><\/span><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span>&nbsp; <\/span>module Exception # Reopening CLR&#8217;s System.Exception<\/p>\n<p><\/span><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>#This is equivalent to defining properties x and y in c#<\/p>\n<p><\/span><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>def x<\/p>\n<p><\/span><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>@x<\/p>\n<p><\/span><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>end<\/p>\n<p><\/span><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>def x=(val)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>@x=val<\/p>\n<p><\/span><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>end<\/p>\n<p><\/span><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>def y<\/p>\n<p><\/span><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>@y<\/p>\n<p><\/span><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>end<\/p>\n<p><\/span><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>def y=<\/p>\n<p><\/span><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>@y=val<\/p>\n<p><\/span><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>end<\/p>\n<p><\/span><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span>&nbsp; <\/span>end<\/p>\n<p><\/span><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span>end<\/p>\n<p><\/span><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span>e1 = System::Exception.new<\/p>\n<p><\/span><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span>e2 = System::Exception.new<\/p>\n<p><\/span><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span># This causes a circular reference: <\/p>\n<p><\/span><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span># e1.x points to e2 (and e2.y), and e2.y points to e1 (and e1.x)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span>e1.x = e2<\/p>\n<p><\/span><\/p>\n<p class=\"MsoListParagraphCxSpLast\"><span>e2.y = e1<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\"><span>The instance variables in this example create a circular reference between the keys and values. <\/span><span lang=\"EN-GB\">This is an issue in practice; hence the request for a CLR provided solution. Leaking a little memory doesn&rsquo;t affect the correctness of the program, but it definitely isn&rsquo;t an ideal situation.<\/span><span><\/p>\n<p><\/span><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN-GB\"><font size=\"3\" face=\"Calibri\">You might be wondering how <\/font><\/span><span lang=\"EN-GB\">ConditionalWeakTable&lt;TKey, TValue&gt; <\/span><span lang=\"EN-GB\"><font size=\"3\" face=\"Calibri\">is different from the<\/font><\/span><span lang=\"EN-GB\"> <\/span><span>Dictionary&lt;TKey,TValue&gt; <\/span><span lang=\"EN-GB\"><font size=\"3\" face=\"Calibri\">class.<\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN-GB\"><font size=\"3\" face=\"Calibri\">Unlike a <\/font><\/span><span>Dictionary&lt;TKey,TValue&gt;,<\/span><span lang=\"EN-GB\"><font size=\"3\" face=\"Calibri\"> the keys in the weak table are weak references and the values<span>&nbsp; <\/span>are kept alive only if the corresponding object key is alive. This means that a key whose value has no references outside the conditional weak table is considered dead and the dictionary automatically removes the key\/value entry after GC happens.<\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN-GB\"><font size=\"3\"><font face=\"Calibri\"><span>&nbsp;<\/span>Also unlike <\/font><\/font><\/span><span>Dictionary&lt;TKey,TValue&gt;,<\/span><font size=\"3\"><font face=\"Calibri\"> <span lang=\"EN-GB\">the weak table&rsquo;s methods are thread-safe and requires no additional locking to be done by callers. <\/span><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN-GB\"><font size=\"3\" face=\"Calibri\">As stated in the beginning, the conditional weak table is not intended to be a general purpose collection. It does not expose the full public surface area of IDictionary. For example, it&rsquo;s not possible to enumerate over the conditional weak table. If you need dictionary-like functionality in your code you should use the <\/font><\/span><span lang=\"EN-GB\">Dictionary&lt;<\/span><span>TKey,TValue<\/span><span lang=\"EN-GB\">&gt;<\/span><span lang=\"EN-GB\"><font size=\"3\" face=\"Calibri\"> from the System.Collections.Generics namespace. But if you&rsquo;re writing a .NET language of your own and need to expose the ability to attach properties to objects you should definitely look into the Conditional Weak Table.<\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN-GB\"><font size=\"3\" face=\"Calibri\"><\/font><\/span>&nbsp;<\/p>\n<p class=\"MsoNormal\"><span lang=\"EN-GB\"><font size=\"3\" face=\"Calibri\">Diana Milirud,<\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN-GB\"><font size=\"3\" face=\"Calibri\">SDET, CLR<\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN-GB\"><font size=\"3\" face=\"Calibri\"><\/font><\/span>&nbsp;<\/p>\n<p class=\"MsoNormal\"><span lang=\"EN-GB\"><\/p>\n<p><font size=\"3\" face=\"Calibri\">&nbsp;<\/font><\/p>\n<p><\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The Dynamic Language Runtime allows you to implement a dynamic language (such as Ruby or Python) on top of the CLR. There are a lot of challenges to making everything work right. One particularly difficult aspect was enabling Ruby to attach arbitrary &#8220;properties&#8221; to instanced .NET managed objects at runtime. If a Ruby developer sets [&hellip;]<\/p>\n","protected":false},"author":342,"featured_media":58792,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685],"tags":[],"class_list":["post-1793","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet"],"acf":[],"blog_post_summary":"<p>The Dynamic Language Runtime allows you to implement a dynamic language (such as Ruby or Python) on top of the CLR. There are a lot of challenges to making everything work right. One particularly difficult aspect was enabling Ruby to attach arbitrary &#8220;properties&#8221; to instanced .NET managed objects at runtime. If a Ruby developer sets [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/1793","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/342"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=1793"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/1793\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/58792"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=1793"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=1793"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=1793"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}