none
Get the Name of a Property in LINQ Expression Form

    Question

  • There doesn't seem to be a simple way of programmatically retrieving the name of class properties.  Most examples I've seen access the property name using an Expression.  This works fine if you provide the property on an actual instance of a class.  For example, Util.GetPropertyName(() => mySomething.Here).  This doesn't work if you pass GetPropertyName a value already in Linq form.  For example, Util.GetPropertyName(sure(thing)).  What additional GetPropertyName method signature do I need to add that would allow passing sure(thing) to retrieve the property name?

    Thank you for the help.

    Ryan

    namespace A
    {
      public static class Util
      {
        public static string GetPropertyName<T>(Expression<Func<T>> expr)
        {
          return (((MemberExpression)expr.Body).Member).Name;
        }
      }
    
      public class Something
      {
        public string Here
        { get; set; }
      }
    
      public class Program
      {
        public static void GetValue<T>(Something thing, Func<Something, T> sure)
        {
          //Compiles but throws an exception
          return Util.GetPropertyName(() => sure(thing));
    
          //Doesn't compile
          return GetPropertyName(sure(thing)); 
        }
    
        static void Main(string[] args)
        {
          var mySomething = new Something();
    
          //Works Fine
          Console.WriteLine(Util.GetPropertyName(() => mySomething.Here));
    
          //Throws an exception.
          string myString = GetValue(mySomething, s => s.Here);
        }
      }
    }


    • Edited by Ryan Software Wednesday, March 29, 2017 8:50 PM Incorrectly Copied Example
    Wednesday, March 29, 2017 7:09 PM

All replies

  • Can you post a version of that that compiles and shows the error that you're getting.  My guess is that you will need to do some unwrapping in GetPropertyName if you don't pass in a simple MemberExpression.

    David


    Microsoft Technology Center - Dallas
    My blog

    Wednesday, March 29, 2017 8:21 PM
  • I fixed the example that I copied from my Visual Studio Project.

    Instead of "unwrapping" in GetPropertyName how do I wrap the Func<Something, T> into a simple MemberExpression to pass to GetPropertyName?

    Wednesday, March 29, 2017 8:53 PM
  • Here you go.  You just need to keep 's=>s.Here' as an expression, instead of assigning it directly to a Func<Something,T>.  A Func<Something,T> is a bit of compiled code, whereas an Expression<Func<Something,T>> is a bit of data that you can examine (or compile and run).

    eg

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace A
    {
        public static class Util
        {
            public static string GetPropertyName<T>(Expression<Func<Something,T>> expr)
            {
                Expression exp = expr;
    
                while (exp.NodeType != ExpressionType.MemberAccess)
                {
                    var subexp = (exp as LambdaExpression)?.Body
                               ?? (exp as InvocationExpression)?.Expression;
    
    
                    if (exp == null)
                    {
                        throw new NotSupportedException($"Expression type {exp.NodeType} not supported.");
                    }
    
                    exp = subexp;
    
                }
    
                return ((MemberExpression)exp).Member.Name;
            }
            public static string GetPropertyName<T>(Expression<Func<T>> expr)
            {
                Expression exp = expr;
    
                while (exp.NodeType != ExpressionType.MemberAccess)
                {
                    var subexp =  (exp as LambdaExpression)?.Body
                               ?? (exp as InvocationExpression)?.Expression;
    
    
                    if (exp == null)
                    {
                        throw new NotSupportedException($"Expression type {exp.NodeType} not supported.");
                    }
    
                    exp = subexp;
    
                }
    
                return ((MemberExpression)exp).Member.Name;
            }
        }
    
        public class Something
        {
            public string Here { get; set; } = "Value of Here";
        }
    
        public class Program
        {
            public static string GetValue<T>(Something thing, Expression< Func< Something, T>> sure)
            {
                var f = sure.Compile();
                var value = f(thing);
    
                return Util.GetPropertyName(sure);
            }
    
            static void Main(string[] args)
            {
                var mySomething = new Something();
    
                //Works Fine
                Console.WriteLine(Util.GetPropertyName(() => mySomething.Here));
    
                //pass the expression not the func.
                string myString = GetValue(mySomething, s => s.Here);
                Console.WriteLine(myString);
            }
        }
    }
    David


    Microsoft Technology Center - Dallas
    My blog


    Wednesday, March 29, 2017 9:37 PM
  • Thank you for your reply.

    To accomplish your example you had to change the method signature of GetValue.  I appreciate you showing how to get access to func by using .Compile().  This is a problem, though, as I can't change the method signature.

    Is there a way to wrap the Func<Something, T> into an expression and provide that information to GetPropertyName?

    Ryan


    Wednesday, March 29, 2017 10:40 PM
  • As far as I know there's no reasonably way to get this information from a delegate.

    David


    Microsoft Technology Center - Dallas
    My blog

    Thursday, March 30, 2017 12:07 AM
  • As far as I know there's no reasonably way to get this information from a delegate.

    I understand that a delegate doesn't have property name information.  Is there a way to wrap the delegate into an expression?  This way I can keep the method signature of GetValue but pass the "delegate wrapped as an expression" to GetPropertyName to retrieve the property name that's needed.
    Thursday, March 30, 2017 6:22 PM