BuddyJoe
BuddyJoe

Reputation: 71101

Expression parsing - Possible to get array of property names as string?

Is it possible to complete this method? Is it possible in the latest version of C#? Thinking about this as a DSL to configure a system for watching for certain property changes on certain objects.

List<string> list = GetProps<AccountOwner>(x => new object[] {x.AccountOwnerName, x.AccountOwnerNumber}); 
// would return "AccountOwnerName" and "AccountOwnerNumber"

public List<string> GetProps<T>(Expression<Func<T, object[]>> exp)
{  
    // code here
}

Upvotes: 2

Views: 1164

Answers (2)

Jon Skeet
Jon Skeet

Reputation: 1499880

In C# 6, you'd use:

List<string> list = new List<string>
{
    nameof(AccountOwner.AccountOwnerName),
    nameof(AccountOwner.AccountOwnerNumber)
};

Before that, you could certainly break the expression tree apart - the easiest way of working out how is probably to either use an expression tree visualizer, or use the code you've got and put a break point in the method (just make it return null for now) and examine the expression tree in the debugger. I'm sure it won't be very complicated - just a bit more than normal due to the array.

You could possibly simplify it using an anonymous type, if you use:

List<string> list = Properties<AccountOwner>.GetNames(x => new {x.AccountOwnerName, x.AccountOwnerNumber});

Then you could have:

public static class Properties<TSource>
{
    public static List<string> GetNames<TResult>(Func<TSource, TResult> ignored)
    {
        // Use normal reflection to get the properties
    }
}

If you don't care about the ordering, you could just use

return typeof(TResult).GetProperties().Select(p => p.Name).ToList();

If you do care about the ordering, you'd need to look at the names the C# compiler gives to the constructor parameters instead - it's a bit ugly. Note that we don't need an expression tree though - we only need the property names from the anonymous type. (An expression tree would work just as well, admittedly.)

Upvotes: 6

Jcl
Jcl

Reputation: 28272

Without c# 6 and nameof, you could get a property name from a expression tree like:

using System.Linq.Expressions;
//...
static string GetNameOf<T>(Expression<Func<T>> property)
{
  return (property.Body as MemberExpression).Member.Name;
}

Using it like:

GetNameOf(() => myObject.Property);

Not directly usable for an array of objects, but you could make an overload to take an array of expressions... something like:

static string[] GetNameOf(IEnumerable<Expression<Func<object>>> properties)
{
  return properties.Select(GetNameOf).ToArray();
}

And use it like

GetNameOf(
     new Expression<Func<object>>[] 
     { 
       () => x.AccountOwnerName, 
       () => x.AccountOwnerNumber
     }
); 

Demonstrating fiddle: https://dotnetfiddle.net/GsV96t

Update

If you go this route, the original GetNameOf for a single property won't work for value types (since they get boxed to object in the Expression and now the expression uses Convert internally). This is easily solvable by changing the code to something like:

static string GetNameOf<T>(Expression<Func<T>> property)
{
  var unary = property.Body as UnaryExpression;
  if (unary != null)
    return (unary.Operand as MemberExpression).Member.Name;
  return (property.Body as MemberExpression).Member.Name;
}

Updated fiddle: https://dotnetfiddle.net/ToXRuu

Note: in this updated fiddle I've also updated the overloaded method to return a List instead of an array, since that's what was on your original code

Upvotes: 2

Related Questions