AOP – string interning with PostSharp attribute

Whilst search­ing for an ele­gant solu­tion to apply string intern­ing across a large num­ber of class­es (we’re talk­ing about hun­dreds of class­es here..) it dawned on me that I can achieve this with ease using PostSharp’s Loca­tion­In­ter­cep­tionAspect. All I need­ed 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­struct­ed is interned, includ­ing string prop­er­ties and fields defined by its sub­class, which is exact­ly 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­er­ty has been mod­i­fied to invoke the OnSet­Val­ue 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­er­ty to that ref­er­ence.

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

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 class­es are also sub­ject to the same string intern­ing treat­ment with­out us hav­ing to explic­it­ly apply the Inter­nAt­tribute on them:

image

 

F# Compatible

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

image

If you look at the gen­er­at­ed C# code for the dis­crim­i­nat­ed union type, the inter­nal MyDuType.CaseB type would look some­thing like the fol­low­ing:

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­struct­ed, it is the inter­nal prop­er­ties whose val­ues are ini­tial­ized:

image

 

Final­ly, let’s look at the record type, which inter­est­ing­ly 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­cut­ed as part of the post-com­pi­la­tion weav­ing process as opposed to run­time), so the inter­nal rep­re­sen­ta­tion of the Age field is left unal­tered.

The Name field, how­ev­er, being of string type, was sub­ject to the same trans­for­ma­tion as all our oth­er exam­ples.

 

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