views:

249

answers:

5

Assumptions

Suppose I have a class with a property:

class ClassWithProperty
{
    public string Prop { get; private set; }

    public ClassWithProperty(string prop)
    {
        this.Prop = prop;
    }
}

And now suppose I have created an instance of that class:

var test = new ClassWithProperty("test value");

What I want

I want this printed out to the console:

Prop = 'test value'

Piece of cake! I use this code to produce the wanted output:

Console.WriteLine("Prop = '{1}'", test.Prop);

Problem

I am violating DRY, because "Prop" appears twice in the code above. If I refactor the class and change the name of the property, I also have to change the string literal. There is also a lot of boilerplate code, if I had a class with many properties.

Proposed solution

string result = buildString(() => c.Prop);
Console.WriteLine(result);

Where the buildString method looks like this:

private static string buildString(Expression<Func<string>> expression)
{
    MemberExpression memberExpression = (MemberExpression)expression.Body;
    string propertyName = memberExpression.Member.Name;

    Func<string> compiledFunction = expression.Compile();
    string propertyValue = compiledFunction.Invoke();

    return string.Format("{0} = '{1}'", propertyName, propertyValue);
}

Question

The above solution works fine and I am happy with it, but if there is some more easier and less "scary" way to solve this, that would make me much happier. Is there some easier alternative to achieve the same result with less and simpler code? Maybe something without expression trees?

Edit

Based on Manu's fine idea (see below) I could use this extension method:

static public IEnumerable<string> ListProperties<T>(this T instance)
{
    return instance.GetType().GetProperties()
        .Select(p => string.Format("{0} = '{1}'",
        p.Name, p.GetValue(instance, null)));
}

It's great for getting a string representation for all properties for an instance.

But: From this enumeration, how could I pick type-safely a specific property? Again I would use expression trees ... or am I not seeing the wood for the trees?

Edit 2

Using reflection or expression trees here is a matter of taste.

Luke's idea using projection initializers is just brilliant. From his answer I finally build this extension method (which is basically just a LINQ'd version of his answer):

public static IEnumerable<string> BuildString(this object source)
{
    return from p in source.GetType().GetProperties()
           select string.Format("{0} = '{1}'", p.Name, p.GetValue(source, null));
}

Now a call will look like this:

new { c.Prop }.BuildString().First()

Which looks a bit nicer than the my original call with a lambda (but this is also a matter of taste I guess). Luke's suggestion is however superior to my solution, since it allows specifying as many properties as you like (see below).

+1  A: 

Reflection?

flesh
Yes, it's an alternative, but I think that code would be less simpler than using expression trees. Please post an example code, if I am wrong.
Theo Lenndorff
I wouldn't disagree, I guess it's personal preference whether you prefer reflection over expression trees.
flesh
Aaahh ... Manu has an example. Yes, it's a matter of taste then. ;-)
Theo Lenndorff
+1  A: 

I've seen it done with a delegate and IL interrogation, but that isn't exactly simpler. In short, no: there is no infoof in C#. Here's Eric Lippert's take on this: In Foof We Trust: A Dialogue

Marc Gravell
Just googled a bit for this and also skimmed through that link (hard to understand, guess have to read it twice ;-)). Where does this concept come from? C, Smalltalk? Have to admit that I never heard of it.
Theo Lenndorff
+2  A: 
var listOfPropertyNamesAndValues = this.GetType().GetProperties()
    .Select(prop => string.Format("{0} = '{1}'", 
    prop.Name, prop.GetValue(this,null)));

If you want to fetch a specific Property, you'll have to pass it's name as a string and get it via reflection (just add a where clause to above query). The good news are that you are not violating DRY anymore, the bad news are that it's not typesafe.

Manu
typeof(this) eeeeeeeeeeeeeeeeeeeekkkkkkkkkk!! ;p
leppie
@leppie: You're right. My mistake. I hope it's better now.
Manu
@Manu: Thanks for that solution. It's very useful. I already have incorporated your suggestion into the question, but I stumbling over some minor issue.
Theo Lenndorff
+2  A: 

You could pass an anonymous type to a BuildStrings method, taking advantage of projection initializers to automatically create the names and values of the anonymous type's properties. Inside the method you would use reflection to interrogate those properties.

This would also allow you to pass multiple items if you wished. You could also pass fields and locals as well as properties, because they'd all be projected as properties of the anonymous type that can then be interrogated with GetProperties etc. (Of course, all that the method is actually doing is enumerating all properties of the object that's passed-in. You could pass any type.)

string result = BuildStrings(new { test.Prop }).First();
Console.WriteLine(result);

// or

string foo = "Test";
int bar = 42;

string results = BuildStrings(new { foo, bar, test.Prop });
foreach (string r in results)
{
    Console.WriteLine(r);
}

// ...

public static IEnumerable<string> BuildStrings(object source)
{
    return source.GetType().GetProperties().Select(
        p => string.Format("{0} = '{1}'", p.Name, p.GetValue(source, null)));
}
LukeH
This is a alternative to expression trees with the possibility to pick a specific property. I like it ...
Theo Lenndorff
+1  A: 

I actually like your original idea of using an expression tree. It's a good use case for ET. But you make it look a little bit scary because you compile and execute an expression tree to get the value of the property, while all you need is just the name. Make the method return only the name and then use it as you did in your first "non-DRY" attempt.

    private static string buildString(Expression<Func<string>> expression) 
    { 
        MemberExpression memberExpression = (MemberExpression)expression.Body; 
        return memberExpression.Member.Name; 
    }

And then

        Console.WriteLine("{0} = {1}", buildString(() => c.Prop), c.Prop);

It doesn't look that scary. Yes, you use c.Prop twice here, but I think you get a significant performance improvement, because you don't need expression tree compilation. And you still don't use any string literals.

Alexandra Rusina