The SR pattern, a simple approach to resource handling

Note: this entry has moved.

 

I’m currently performing a huge code & architecture review of a very big .NET project that includes several web applications and several frameworks. One of the first things I did was to estimate how costly would be to make all this stuff I18N ready. To my surprise (ok, not much of a surprise…) I found as many different ways to handle I18N as different teams the project has; moreover, most of them were really ugly ones… and some teams didn’t even thought about I18N.

 

Regarding resource handling, I decided to write a doc explaining the SR type that is used all along the .NET fx and also in other Microsoft products like Commerce Server, Exchange, etc, to present it to each team as “The SR pattern, a proven way to resource handling”. After that, I translated it to english and… this post was borned.

 

Hey look, it’s a double-check locking!

 

SR is an internal sealed type (I’m guessing named SR after “String Resources” although it can handle more than just strings). This type implements the double-check locking pattern, which is just a singleton pattern with thread safety in mind: it doesn’t expose any public constructors and it has a static method that always returns the very same instance of the type, plus it contains proper checks to make it thread-safe:

 

internal sealed class SR {

     private static SR loader;

          static SR () {

          SR.loader = null;

     }

     static SR GetLoader () {

          if (SR.loader == null) {

                lock (typeof(SR)) {

                     if (SR.loader == null)

                          SR.loader = new SR ();

                }

          }

          return SR.loader;

     }

}

 

By following the advices in CBrumme’s Memory Model post you could make this double checking safer by using a memory barrier after construction and before assignment of loader, i.e.:

 

SR sr = new SR ();
Thread.MemoryBarrier ();
SR.loader = sr;

 

Ok, enough singleton stuff, let’s get back on topic now.

 

Where to put the neutral culture resources?

 

The most common approach is to include the neutral culture resources into your main assembly. Following the approach taken by the .NET framework itself, a resource manager is instantiated and cached in the SR type’s internal constructor:

 

internal SR () {

     this.resMgr = new ResourceManager ("YourRootName", this.GetType().Assembly);

}

 

Note that the same assembly containing the SR type was specified as the one where the resource manager should look for resources. This is a good choice, as resources for a type are deployed along with it into the same assembly instead of going into a separate one, plus it enforces a bit the concept that a type’s resources are only meant to be used by that type and no other.

 

An alternative approach would be to ship the resources for the neutral culture in its own satellite assembly, i.e.: YourCompany.YourProduct.CommonStrings.dll, as some of the above mentioned Microsoft products actually do; but I prefer the .NET framework approach instead. Lastly, each additional supported culture should make it into its own satellite assembly.

 

No need to reinvent .resx

 

When doing the code review mentioned above, I stumped against some code of a guy that decided to create his very own resource format. You don’t need to do that, really; just add an Assembly Resource File (.resx) to your project and you’re done.

 

Sadly, VS.NET support for editing .resx is almost non-existent; it may be ok to use the XML view if you’re only dealing with strings but you’re on your own for anything else. Luckily you will find some free apps and add-ins to fill in this gap.

 

This is how a string resource looks like:

 

<data name="Attr_not_supported_in_directive">

          <value>The '{0}' attribute is not supported by the '{1}' directive.</value>

     </data>

 

The value for the name attribute of the data element represents the resource ID that we will need to provide when looking to access a resource’s value, which we provided as the content of the value element. Note that the {0} and {1} placeholders are there so we can easily format the string later using String.Format.

 

 

Naming resources

 

Regarding the naming of resources IDs, the framework tends to compose it using the name of the control or component to which the message belongs to, plus the description of the message itself, separating them with an underscore character, i.e.:

 

Calendar_TitleFormat

 

Actually, you will see that an underscore character is used also for separating words in the message detail, i.e.:

 

Object_tag_must_have_class_classid_or_progid

 

Seven underscores seems like just too much to me, so replacing them with some PascalCasing may be a good idea.

 

Get me that string, please

 

After you’re done creating your .resx file you need an easy and error-free way to get strings from your application (or component, or framework, or whatever you happen to be coding at the time!). SR tackled this by defining internal constant strings holding each resource’s ID:

 

internal sealed class SR {

     internal const String Calendar_TitleFormat = “Calendar_TitleFormat”;

     internal const String Assembly_not_compiled = “Assembly_not_compiled”;

     .

     .

     .

 

And by offering a bunch of utility methods to easily get resources of different types, i.e.:

 

static public string GetString (CultureInfo ci, string resid) {

     return SR.GetLoader ().resMgr.GetString (resid, ci);

}

 

static public int GetInt (CultureInfo ci, string resid) {

     int num = 0;

      object o = SR.GetObject (ci, resid);

     if (o != null)

     num = (int) o;

     return num;

}

 

 

Then you would always use the previously defined constant strings (just type “SR.” and intellisense will do the rest) as the parameters to feed the GetXXX methods, i.e.:

 

string errmsg = SR.GetString (SR.Assembly_not_compiled);

 

 

Giving a clue to the Resource Manager

 

The NeutralResourcesLanguageAttribute attribute is used to mark an assembly with the language used in writing the neutral culture; the resource manager will then use the value of this attribute as a hint to speed up the search when looking up resources in the same culture as the neutral one, saving any trips to satellite assemblies.

 

 

This should be enough of an intro to the SR type and a good starting point to a simple way of handling resources; you can download some skeleton code from here.

 

There are some implementation details about SR that I don’t like and some curiosities also that I haven’t touched here just to not bloat this post, but I will do so at my next post, hopefully within this year… :-)  

4 Comments

  • Updated now to reflect CBrumme's advices plus some code,



    Thanks!

  • Oh my god...

    lock (typeof(SR))



    this is EVIL EVIL EVIL EVIL.



    The code should be refactored to provide



    private static object SyncRoot = new object();



    and lock(SyncRoot)



    The blogosphere has been vocal enough about how locking on a type is a bad thing, and on how microsoft documentation providing examples of locking Type objects was a bad thing.

  • Thanks for commenting!



    You're right and thats already fixed in the ... ooops I'm not supposed to talk about this yet. The refectoring you mention plus CBrumme's suggestions are already implemented in ... ooops here I go again.



    I already wrote a post detailing how SR is implemented in the whidbey bits and the changes made. Because of NDA's I can't publish it until after the PDC I believe.

  • Why do you use the singleton pattern, why not just make ResourceManager a static member of the class?

Comments have been disabled for this content.