Whilst search­ing for an ele­gant solu­tion to apply string intern­ing across a large num­ber of classes (we’re talk­ing about hun­dreds of classes here..) it dawned on me that I can achieve this with ease using PostSharp’s Loca­tion­In­ter­cep­tionAspect. All I needed was some­thing along the lines of:

You can apply this attribute to a class or even a whole assem­bly and it’ll ensure every piece of string con­structed is interned, includ­ing string prop­er­ties and fields defined by its sub­class, which is exactly what I was after.

For exam­ple, take this triv­ial piece of code:

image

If you inspect the com­piled code for the Base class in ILSpy you will see some­thing along the lines of:

image

notice how the set­ter for BaseS­tring­Prop­erty has been mod­i­fied to invoke the OnSet­Value method defined in our aspect above as opposed to the set­ter method. In this case, it’ll call the String.Intern method to retrieve a ref­er­ence to an interned instance of the string and set the prop­erty to that reference.

For more details on PostSharp’s inter­cep­tion aspects, I rec­om­mend read­ing Dustin Davis’s excel­lent posts on the topic:

Post­Sharp Prin­ci­ples: Day 7 Inter­cep­tion Aspects – Part 1

Post­Sharp Prin­ci­ples: Day 8 Inter­cep­tion Aspects – Part 2

 

As we’ve spec­i­fied the mul­ti­cast inher­i­tance behav­iour to mul­ti­cast the attribute to mem­bers of the chil­dren of the orig­i­nal ele­ment, the string prop­er­ties defined in both A and B classes are also sub­ject to the same string intern­ing treat­ment with­out us hav­ing to explic­itly apply the Inter­nAt­tribute on them:

image

 

F# Com­pat­i­ble

What’s more, this attribute also works with F# types too, includ­ing record and dis­crim­i­nated unions types. Take for instance:

image

If you look at the gen­er­ated C# code for the dis­crim­i­nated union type, the inter­nal MyDuType.CaseB type would look some­thing like the following:

image

notice how the two inter­nal item1 and item2 properties’s set­ter meth­ods have been mod­i­fied in much the same way as the C# exam­ples above? The pub­lic Item1 and Item2 prop­er­ties are read-only and get their val­ues from the inter­nal prop­er­ties instead.

Indeed, when a new instance of the CaseB type is con­structed, it is the inter­nal prop­er­ties whose val­ues are initialized:

image

 

Finally, let’s look at the record type, which inter­est­ingly also defines a non-string field:

image

because we have spec­i­fied that the Inter­nAt­tribute should only be applied to prop­er­ties or fields of type string (via the Com­pile­TimeVal­i­date method which is exe­cuted as part of the post-compilation weav­ing process as opposed to run­time), so the inter­nal rep­re­sen­ta­tion of the Age field is left unaltered.

The Name field, how­ever, being of string type, was sub­ject to the same trans­for­ma­tion as all our other examples.

 

I hope this lit­tle attribute can prove to be use­ful to you too, it has cer­tainly saved me from an unbear­able amount of grunt work!

Share

4 Responses to “AOP – string interning with PostSharp attribute”

  1. Sprague says:

    What would be the pur­pose of imple­ment­ing cus­tom string intern­ing? I believe this is the default behav­iour in new .NET ver­sions. Please for­give me if this is a naive question.

    Not a crit­i­cism, just won­der­ing! Great article.

  2. theburningmonk says:

    @Sprague:

    No wor­ries, I wel­come con­struc­tive criticism :-)

    The CLR interns string lit­er­als only, not dynam­i­cally cre­ated strings. For instance, if you were to read a bunch of data from XML/JSON/etc. the string data that you come across will not be interned.

    When you’re deal­ing with large num­bers of strings in your appli­ca­tion and many of them are dupli­cates (keys to ref­er­ence other domain objects with, for instance) then this can help reduce your mem­ory footprint.

    Also, if you find that many of these dupli­cates live beyond Gen0 col­lec­tions then it could also have a pos­i­tive impact on the GC with less data to col­lect or move dur­ing compaction.

  3. Sprague says:

    @theburningmonk

    This makes sense as string intern­ing is a fea­ture on the C# com­piler. I haven’t been able to find any infor­ma­tion online say­ing that string intern­ing isn’t avail­able in CLI in gen­eral, but it makes sense… I won­der how many peo­ple I’ve given bad advice to :X

  4. theburningmonk says:

    @sprague

    I remem­ber read­ing about the string intern­ing behav­iour on Eric Lippert’s a blog quite some time ago, and the MSDN doc­u­men­ta­tion on the String.Intern method touched on it too.

    Per­haps I should put together a follow-up post with exam­ples before and after apply­ing the attribute (you can check if a string is in the pool by using String.IsInterned and check­ing its result, which would be null if the string is not interned) and maybe some pro­fil­ing results when deal­ing with rea­son­ably large data sets with lots of dupli­cated strings.

Leave a Reply