How to Enumerate Strings using Resource Files in C#

February 14, 2010

Strings are not valid types within enumerations. So the coder usually has to create a whole bunch of constant string values and simply have them grouped together within their classes. It’s not very desirable for a few reasons. For starters, there is no enforcement that a string value has to belong to a particular class, and also, they are much harder to read in from resource files.

Enumerations are an excellent way to make code more reliable, readable and modifiable. They are a special case, where every value of a variable is already known, and you want to be able to describe them in a more readable way. They allow each value to be grouped together so that there is a reduced likelihood that an incorrect value is specified in code.

For example,

public enum Flowers
{
   Rose,
   Tulip,
   Daisy,
   Daffodil
}

If you declare a variable of type Flowers, then you know that only Rose, Tulip, Daisy, Daffodil can be selected.

To do the equivalent with strings would require, perhaps, a class

public class Vehicles
{
    public const string AlfaRomeo = "Alfa Romeo";
    public const string MercedesBenz = "Mercedes Benz";
    public const string Ford = "Ford";
    public const string Porche = "Porche";
    public const string AstonMartin = "Aston Martin";
}

But there is nothing to prevent you from using a string from the wrong class in your code. If you declare a variable for the car

string myCar;

There is nothing to prevent setting the value to something other than what exists in the class Cars.

myCar = "Honda";

Another major benefit of enumeration is that non-domain errors are caught at compile time, not runtime. Finding errors at compile time is significantly cheaper to resolve than finding the error after it has gone live. So it is highly desirable to find these sorts of errors as early as possible in the process.

And it can be done!

I will demonstrate how to better group your strings, and also to treat those strings as enumerations. Here, I will use attributes and reflection to retrieve the values from resource files.

So to start with, create a new string resources file. I have called mine MessageResources. Add a few strings to the resource file. I have added mine as follows:

Message Resources File containing Vehicle strings

Note that I have deliberately chosen names that include spaces. When you use the ToString() method on a standard enumeration, expecting to see their values, it is significantly harder to use those values in, say, a combo box, if they don’t have spaces in them.

Now let me skip to the enumeration itself.

Start with a typical enumeration:

public enum Vehicles
{
    AlfaRomeo,
    MercedesBenz,
    Ford,
    Porche,
    AstonMartin
}

Of course, you need to link this to the resource file, so we’ll do that via attributes. So I will create an attribute for the Enumeration, as follows:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EnumHelperSample
{
    public class EnumAttribute : Attribute
    {
        public Type ResourceType { get; private set; }
        public string ResourceName { get; private set; }
        public int SortOrder { get; private set; }

        public EnumAttribute(Type ResourceType,
                             string ResourceName,
                             int SortOrder)
        {
            this.ResourceType = ResourceType;
            this.ResourceName = ResourceName;
            this.SortOrder = SortOrder;
        }
    }
}

I have decided to include the type of the resource file that contains the strings, the name of the string resource, and the order that I would like the enumerations to be sorted if returned as a list of strings.

However, this won’t work. If you have a look in the MessageResources.Designer.cs file, this generates the following code:


/// <summary>
///   Looks up a localized string similar to Alfa Romeo.
/// </summary>
internal static string AlfaRomeo {
   get {
       return ResourceManager
              .GetString("AlfaRomeo", resourceCulture);
   }
}

For a value to be passed via an attribute, it needs to be a constant, and the MessageResources generated code does not provide a constant string.

Enter Dmytro Kryvko and the ResXFileCodeGeneratorEx Custom Tool.

Dmytro has replaced the custom tool found on the standard Resources file with a new custom tool that he wrote. This tool generates a constant string for every resource name that is generated within the resource file.

You can download it from here:
http://www.codeproject.com/KB/dotnet/ResXFileCodeGeneratorEx.aspx

If you really want to go and improve his code, by all means, go ahead. For our purposes, it will be fine simply to run the installer.

After installing this on your machine, you select properties on the resources file, and change the custom tool to the extended version, ending in Ex.

Changing Message Resources Custom Tool to ResXFileCodeGeneratorEx

Now we can pass the resource names as constants.

The enumeration code then becomes:

namespace EnumHelperSample
{
    public enum Vehicles
    {
        [EnumAttribute(typeof(MessageResources),
                       MessageResources.ResourceNames.AlfaRomeo,
                       1)]
        AlfaRomeo,
        [EnumAttribute(typeof(MessageResources),
                       MessageResources.ResourceNames.MercedesBenz,
                       4)]
        MercedesBenz,
        [EnumAttribute(typeof(MessageResources),
                       MessageResources.ResourceNames.Ford,
                       3)]
        Ford,
        [EnumAttribute(typeof(MessageResources),
                       MessageResources.ResourceNames.Porche,
                       5)]
        Porche,
        [EnumAttribute(typeof(MessageResources),
                       MessageResources.ResourceNames.AstonMartin,
                       2)]
        AstonMartin
    }

}

Ok, that’s all very well, but how do we retrieve the values?

Well, I have written an EnumHelper class as a set of extensions to retrieve those values.

This first one is to retrieve a value from the associated resource. I do this by extending the Enum type. It allows me to write the following syntax:

Debug.WriteLine(Vehicles.AlfaRomeo.GetString());
Debug.WriteLine(Vehicles.MercedesBenz.GetString());

This outputs:

Alfa Romeo
Mercedes Benz

public static string GetString(this Enum value)
{
    EnumAttribute ea =
               (EnumAttribute)value.GetType()
                .GetField(value.ToString())
                .GetCustomAttributes(typeof(EnumAttribute), false)
                .FirstOrDefault();
     if (ea != null)
     {
           PropertyInfo pi = ea.ResourceType
                               .GetProperty("ResourceManager");
            if (pi != null)
            {
                ResourceManager rm = (ResourceManager)pi
                                     .GetValue(null, null);
                return rm.GetString(ea.ResourceName);
            }
     }

     return string.Empty;
}

And finally to retrieve the entire sorted list, I extended Type itself, as follows:

public static IList GetStrings(this Type enumType)
{
    SortedList<int, string> stringList = new SortedList<int, string>();
    FieldInfo[] fiArray = enumType.GetFields();
    foreach (FieldInfo fi in fiArray)
    {
        EnumAttribute ea =
            (EnumAttribute)fi
                 .GetCustomAttributes(typeof(EnumAttribute), false)
                 .FirstOrDefault();
         if (ea != null)
         {
              PropertyInfo pi = ea.ResourceType
                                  .GetProperty("ResourceManager");
              if (pi != null)
              {
                  ResourceManager rm = (ResourceManager)pi
                                        .GetValue(null, null);
                  stringList.Add(ea.SortOrder,
                                 rm.GetString(ea.ResourceName));
              }
        }
    }
    return stringList.ToList();
}

So to display the entire sorted list of values:

IList result = typeof(Vehicles).GetStrings();
foreach (KeyValuePair<int,string> kvp in result)
{
    Debug.WriteLine(kvp.Value);
}

Finally, a note. Allowing the enumerations to be specified via Resource Name constants passed as attributes still requires the use of strings. This means other string constants could be passed in to the attribute types.

I believe that the likelihood of this occurring is significantly less of a risk than if the developer was loading up and passing around string constants themselves. And anyway, you’ll need to know which resource file that the strings are coming from. If you are that worried about it, you could always modify Dmytro’s code!

Meanwhile, my code may be found here:

EnumHelperSample.zip


How to add a ToNumber C# language extension method to the String class calling TryParse using reflection.

February 25, 2009

I don’t particularly like the way data conversion between strings and numbers is done in C#. Of course originally we had the Parse method on each type. The down side to that was that it raised an unnecessary exception which needed to be handled, together with the exception’s associated overhead.

 

Next came TryParse. It was far better, because instead of raising an exception, it simply returned a true or false as to whether the string was indeed the type that we were trying to convert to. We usually end up with something like the following code:

int intResult;
bool successfulIntParse = int.TryParse(inputString, out intResult);
if (successfulIntParse)
{
     //do something with intResult
}

We’ve been able to use ToString() on number types for some time, so why not turn it around and execute a ToNumber() method on any string type?

 

The problem, of course, is what to do when the data conversion fails. Value types can’t accept null values. Well, that’s if you really need value types. With the new nullable types, we can provide a ToNumber() method that returns the same type but as a Nullable form.

 

So using C# language extensions, I am able now able to do the following:           
 

string numberString = "12345";
int? intResult = numberString.ToNumber<int>();

The benefit here is that behind the scenes it is still performing the TryParse, however now I can declare a variable inline. If the parse fails, it simply returns a null.

 

Here is the code for my conversion method extension, funnily enough, called ToNumber.

// Copyright 2009, Software Clearing House Pty Ltd
using System;
using System.Reflection;
namespace StringExtensions
{
    public static class StringToNumberCastExtensions
    {
        public static T? ToNumber<T>(this string input) where T : struct
        {
            Type[] paramTypes = new Type[] { typeof(string), typeof(T).MakeByRefType() };
            MethodInfo method = typeof(T).GetMethod("TryParse", paramTypes);
            Object[] parameters = new Object[] { input, null };
            if (method != null)
            {
                bool val = (bool)method.Invoke(typeof(T), parameters);
                if (val == true)
                    return (T?)parameters[1];
            }
            return null;
        }
    }
}

Note the “where T : struct” statement. That’s how I force it to accept only value types.

 

I also use reflection to call the TryParse method on the particular type that is passed in.  Note that this routine shouldn’t bomb if the TryParse method doesn’t exist on the particular value type passed in. Instead, it will return null.                                  

So add this class to your project, include a usingStringExtensions; statement, and you get the benefit of being able to inline convert your strings to numbers.

I also tried to override the cast operator so that I could specifically cast a string to a number, such as

int? myNumber = (int?)”12345″;

but unfortunately C# language extensions doesn’t support extending on operators.

And if you could do this:

int? myNumber = “12345”;

and have it do the automatic conversion, well, that’d be too much like VB, wouldn’t it!