Magic Strings in C# vermeiden

Ich wurde schon öfter im Zusammenhang gefragt (insbesondere im Forum myCSharp.de), wie ich sogenannte Magic Strings vermeide.

Was ist ein Magic String?

Ein Magic String ist quasi einer im Quellcode fest geschriebener Text, der zum Beispiel die Eigenschaft einer Klasse repräsentiert.

Beispiel:

public class Person
{
    public String Name { get; set; }
    public String Nachname { get; set; }
    // Andere Eigenschaften
}
public static void Main( string[ ] args )
{
    var person = new Person( );
    if ( String.IsNullOrEmpty( person.Name ) )
    {
        Console.WriteLine( "Name ist leer!" );
    }
}

Problem: Refaktorierung

Wenn nun die Eigenschaft Name in Vorname geändert wird (Visual Studio > Umbenennen) wird Name im Console.WriteLine() nicht beachtet!

Die Lösung: Reflection

Folgendes Snippet hilft nun dabei, dieses Problem zu vermeiden.

public static class StaticReflectionHelper
{
    public static string GetMemberName<T>( this T source, Expression<Func<T, Object>> exp )
    {
        return GetMemberName( exp );
    }
    public static string GetMemberName<T>( Expression<Func<T, Object>> exp )
    {
        if ( exp == null )
        {
            throw new ArgumentNullException( "exp" );
        }
        return GetMemberNameByExpression( exp.Body );
    }
    public static String GetMemberName<T>( Expression<Action> exp )
    {
        if ( exp == null )
        {
            throw new ArgumentNullException( "exp" );
        }
        return GetMemberNameByExpression( exp.Body );
    }
    private static String GetMemberNameByExpression( Expression exp )
    {
        if ( exp == null )
        {
            throw new ArgumentNullException( "exp" );
        }
        var memExp = exp as MemberExpression;
        if ( memExp != null )
        {
            return GetMemberNameByMemberExpression( memExp );
        }
        var medCaExp = exp as MethodCallExpression;
        if ( medCaExp != null )
        {
            return GetMemberNameByMethodCallExpression( medCaExp );
        }
        var unExp = exp as UnaryExpression;
        if ( unExp != null )
        {
            return GetMemberNameByUnaryExpression( unExp );
        }
        throw new InvalidOperationException( "Invalid expression" );
    }
    private static String GetMemberNameByMemberExpression( MemberExpression memExp )
    {
        return memExp.Member.Name;
    }
    private static String GetMemberNameByMethodCallExpression( MethodCallExpression medCaExp )
    {
        return medCaExp.Method.Name;
    }
    private static String GetMemberNameByUnaryExpression( UnaryExpression unExp )
    {
        var medCaExp = unExp.Operand as MethodCallExpression;
        return medCaExp != null ? medCaExp.Method.Name : ( ( MemberExpression ) unExp.Operand ).Member.Name;
    }
}

Die Verwendung wäre:

public static void Main( string[ ] args )
{
    var nameField = StaticReflectionHelper.GetMemberName<Person>( x => x.Name );
    var person = new Person( );
    if ( String.IsNullOrEmpty( person.Name ) )
    {
        Console.WriteLine( nameField + " ist leer!" );
    }
}