views:

1447

answers:

5

In my code there are several strings which are used as keys to access resources. These keys have a specific format, e.g.

string key = "ABC123";

Currently, all these keys are stored as strings, but I'd like to make things more robust and type-safe. Ideally, I'd like to check the strings are in the correct format at compile time.

The next stage is to create a ResourceKey class that is initialised from a string. I can then check the format of the string at runtime, e.g.

ResourceKey key = "ABC123";

where ResourceKey is defined as:

using System.Diagnostics;
using System.Text.RegularExpressions;

class ResourceKey
{
    public string Key { get; set; }

    public static implicit operator ResourceKey (string s)
    {
        Debug.Assert(Regex.IsMatch(s, @"^[A-Z]{3}[0-9]{3}$"));
        return new ResourceKey () { Key = s };
    }
}

What I'd really like to do is to have a sort of compile-time assert so that the program fails to build if anyone tries to use an invalid key. e.g.

ResourceKey k1 = "ABC123"; // compiles
ResourceKey k2 = "DEF456"; // compiles
ResourceKey k3 = "hello world"; // error at compile time

Is there any way to achieve this?

Thanks

+7  A: 

You could check the values with a unit test. One of my co-workers had to do something similar to this in a project where we needed to ensure that all classes in a certain namespace had certain attributes applied to them.

Run the unit tests with your build (you do that anyways right? :) or as part of an integration build. This will keep your source cleaner as well as you won't have to introduce code that does assertions.

Andrew Hare
I agree on this, I suggest also to check http://www.postsharp.org/for doing ensure/after logic
kentaromiura
Good idea - but unfortunately these keys are spread amongst tens of thousands of lines of legacy code - the vast majority isn't covered by unit tests. We're getting there though!
roomaroo
Sorry to hear about that!
Andrew Hare
+1  A: 

Do you really want these keys hardcoded into your app? Wouldn't it be better to have them in a config file? Then if there are any problems post compile, it's simply a runtime configuration issue.

AdamRalph
Another advantage of an XML based store for the key names would be that the file could always be validated against a Schema.
Cerebrus
The keys are references to entries in an XML file. If all your settings are in a config file, you still need a way to specify which setting to look at.
roomaroo
Since it appears as though these are keys to a resource file, putting them in a config means that you will just need another set of hardcoded strings to get those values. At some point you have to hardcode a string key into the code or provide it as input which wouldn't be practical here.
Andrew Hare
If you use a Settings file then the keys to the config elements become methods on the Settings class and therefore will be checked at compile time.
AdamRalph
+2  A: 

I believe that I would add a Settings class and store them there instead of creating a new type. The Settings class can be backed by an application configuration file that will make them easier to change via configuration file changes if needed. If you don't specify them in the configuration file, though, it will use the values you set as defaults.

I'd also go the unit test route. You'll need to use the InternalsVisibleTo attribute in your Assembly.cs file since I don't think that Settings can be used outside the project if you don't.

tvanfosson
+1 for Settings file and InternalsVisibleTo
Andrew Hare
+1  A: 

AdamRalph has a point but the counter point also works, if you get it right at compile time, you will never have run-time config issues (assuming the correct values can't change)

Outside that, C#'s compile time abilities are absolute Junk. Their is next to nothing that can be done at compile time. The best available I known of is the template where clause. If I had to guess, I'd say that this is a intentional design choice by Anders Hejlsberg as it seems to match with the rest of the language

Andrew Hare's point about unittests + reflection is about as good as I'd expect. A co worker of mine uses that to test that any class that could be used in a particular cases correctly implemented some protocol.

BCS
+1  A: 

If the keys are named according to the same rules as C# identifiers, or perhaps even more limiting, and known and finite, you could use an enum:

public enum ResourceKeys
{
    ABC123,
    DEF456
}
Lasse V. Karlsen