How to use nameof for long property-paths to get the "fullnameof"?

Viewed 380

I have following statement:

queryPublications += $" AND CC_Publication__r.CC_External_ID__c IN ({inPublicationExtIds})";

Now i want to use the nameof expression to get compile time safety for the names, for the case that smething will change in future. But the modified code doesn't look nicely anymore:

queryPublications += $" AND {nameof(CC_Publication_Subscription__c.CC_Publication__r)}.{nameof(CC_Publication_Subscription__c.CC_Publication__r.CC_External_ID__c)} IN ({inPublicationExtIds})";

Consider that there are more fields or the property-path(what's the correct term?) is even longer:

AND Class1.Prperty1.Class2.Property2.Class3.Property3 IN

The code becomes really ugly and you can easily make mistakes. You have to repeat the whole part that comes before the current.

Is there any way(extension or technique) to simplify my approach?

Would be great if there was a fullnameof to get the full path.

3 Answers

You can use expression trees to get the name of the members of an expression:

public static string NameOf<T>(Expression<Func<T>> pathExpr)
{
    var members = new Stack<string>();
    for (var memberExpr = pathExpr.Body as MemberExpression; memberExpr != null; memberExpr = memberExpr.Expression as MemberExpression)
    {
        members.Push(memberExpr.Member.Name);
    }
    return string.Join(".", members);
}

If you call it like so:

NameOf(() => CC_Publication_Subscription__c.CC_Publication__r.CC_External_ID__c)

it will return "CC_Publication_Subscription__c.CC_Publication__r.CC_External_ID__c".

This is a build on @ckuri's answer. That answer requires that the first part of the expression be a class instance, a variable. If you don't have an instance of the class (classic nameof() doesn't require an instance) then this version could work.

Usage example:

var x = NameOf<MainType>(m=>m.MidProperty.FinalProperty);

I'll admit, it's not the prettiest. Here's the function definition. Again, heavily borrowed from @ckuri:

private static string NameOf<TRoot>(Expression<Func<TRoot, object>> pathExpression)
{
  var members = new Stack<string>();
  for (
    var memberExpression = pathExpression.Body as MemberExpression;
    memberExpression != null;
    memberExpression = memberExpression.Expression as MemberExpression
  )
  {
    members.Push(memberExpression.Member.Name);
  }
  members.Push(typeof(TRoot).Name);
  return string.Join(".", members);
}

There's always string.Format - you don't always need to use an interpolated string:

queryPublications += string.Format(" AND {0}.{1} IN ({2})", 
                     nameof(CC_Publication_Subscription__c.CC_Publication__r),
                     nameof(CC_Publication_Subscription__c.CC_Publication__r.CC_External_ID__c),
                     inPublicationExtIds);
Related