The most expensive parts of reading resources are probing for satellite assemblies and deserializing objects. The more you can avoid having the ResourceManager do either of those two tasks, the faster your resource reads will be. In this post I'll cover how to reduce assembly probing, and the first thing you can do is use the NeutralResourcesLanguageAttribute. But what is this assembly probing stuff, anyway?
System.Resources currently has no concept of which resources are currently on the machine. ResourceManager will simply go looking to find resources for whatever culture you're in, and if it doesn't find them, it will fall back to the parent culture. So if your culture is fr-CA (French as spoken in Canada), ResourceManager actually looks on disk for an fr-CA satellite assembly. If it doesn't find it, it falls back to fr (just plain old French), and finally will look for the neutral resources. This is good, in that it means it is very easy to add new localized content to your installed app. It's bad because we could attempt several failed assembly loads before actually retrieving the right resources. If your app is running from across the network, every assembly load means a network round trip, and that's really bad.
NeutralResourcesLanguageAttribute The NeutralResourcesLanguageAttribute tells ResourceManager what culture your neutral resources happens to be written in, allowing it to skip some of the probing it would normally do. Assume your neutral resources are in plain old French and are stored in the main assembly. Now if your culture is fr-CA, we look for the fr-CA satellite and fail. Then we fall back, and the NeutralResourcesLanguageAttribute tells us that we can skip "fr" and go directly to the main assembly. We've just saved one assembly probe. If your neutral resources were in fr-CA, then there would be no assembly probes to do.
The config section But what about that case running over the network? Even a single assembly probe could be unacceptable, and for the times you need total control there is a resources config section. It allows you to specify for each assembly exactly which resources are installed, thereby avoiding all assembly probing. ResourceManager will only look for a culture that's on the list... which also means that you can't simply xcopy new resources in and expect them to work anymore. Here's what a sample config file would look like:
<assembly name="mscorlib, Version=..., PublicKeyToken=...">
<assembly name="UserAssembly, ...">
<assembly name="UserAssembly2, ...">
This config section says that for mscorlib, the French resources are installed. For UserAssembly, there are two cultures installed. Notice that mscorlib is a Frameworks assembly, and this section works equally well for it.