使用表达式树检索强类型属性名称

时间:2016-01-09 04:18:45

标签: c# linq lambda expression-trees

我希望使用表达式树来获取域类型的属性名称。 这最终将在EF上下文中用于发送脏字段的更新。 到目前为止,我有以下内容,并希望确认以下是一个不太完整的实现,以及是否有更灵活/更高效的方法来执行相同的操作:

class Message
{
    public int ID
    {
        get;
        set;
    }
    public string Body
    {
        get;
        set;
    }
}

 internal static class Program
 {
    public static string GetPropertyName(Expression<Func<Message,object>> exp)
    {
        string propName = "";

        UnaryExpression ue = exp.Body as UnaryExpression;
        if(ue != null)
        {
            propName = (ue.Operand as MemberExpression).Member.Name;
        }
        MemberExpression me = exp.Body as MemberExpression;
        if(me != null)
        {
            var constExpression = me.Expression as ConstantExpression;
            var field = me.Member as FieldInfo;
            if (constExpression != null) 
                if (field != null) 
                    propName = field.GetValue(constExpression.Value).ToString();

        }
        ConstantExpression ce = exp.Body as ConstantExpression;
        if(ce != null)
        {
            propName =  ce.Value.ToString();
        }
        MethodCallExpression mc = exp.Body as MethodCallExpression;
        if(mc != null)
        {
            //ParameterExpression param = Expression.Parameter(typeof(Message));
            ParameterExpression param = mc.Arguments.OfType<ParameterExpression>().FirstOrDefault();
            propName = Expression.Lambda(exp.Body, param).Compile().DynamicInvoke(new Message()).ToString();
        }
        return propName;
    }
    private static string SomeMethod(Message m)
    {
        return "ID";
    }     
    private static Expression<Func<Message,object>>[] GetExpressions()
    {
        string[] props = new[] { "ID", "Name" };
        var expressions  = 
            props.Select(p =>  
                {
                    Expression<Func<Message,object>> exp = m => p;
                    return exp;
                }).ToArray();
        return expressions;
    }
    public static void Main(string[] args)
    {
        string str = "id";
        Expression<Func<Message, object>> exp1 = m => str;
        Expression<Func<Message, object>> exp2 = m => "id";
        Expression<Func<Message, object>> exp3 = m => m.ID;
        Expression<Func<Message, object>> exp4 = m => SomeMethod(m);
        var expressions = GetExpressions();


        string s = GetPropertyName(exp1);
        s = GetPropertyName(exp2);
        s = GetPropertyName(exp3);
        s = GetPropertyName(exp4);

        foreach (var exp in expressions)
        {
            s = GetPropertyName(exp);
        }
    }
}

SO Post尝试执行类似操作,但似乎并未涵盖上述用例。 注意:使用&#34; nameof&#34;这里不是一个选择。

1 个答案:

答案 0 :(得分:2)

我会用ExpressionVisitor来做。

class Message
{
    public int ID {
        get;
        set;
    }
    public string Body {
        get;
        set;
    }
}

internal static class Program
{
    private static string SomeMethod(Message m) {
        return "ID";
    }
    private static Expression<Func<Message, object>>[] GetExpressions() {
        string[] props = new[] { "ID", "Name" };
        var expressions =
            props.Select(p => {
                Expression<Func<Message, object>> exp = m => p;
                return exp;
            }).ToArray();
        return expressions;
    }

    public class NameResolverExpressionVisitor : ExpressionVisitor
    {
        private Expression<Func<Message, object>> exp;

        public string Name { get; private set; }

        public override Expression Visit(Expression node) {
            var casted = node as Expression<Func<Message, object>>;

            if (casted != null) {
                exp = casted;
            }

            return base.Visit(node);
        }

        protected override Expression VisitMember(MemberExpression node) {
            var constExpression = node.Expression as ConstantExpression;
            var field = node.Member as FieldInfo;

            if (constExpression != null && field != null) {
                Name = field.GetValue(constExpression.Value).ToString();
            }

            return base.VisitMember(node);
        }
        protected override Expression VisitMethodCall(MethodCallExpression node) {
            Name = exp.Compile().DynamicInvoke(new Message()).ToString();

            return base.VisitMethodCall(node);
        }

        protected override Expression VisitUnary(UnaryExpression node) {
            var memberExpression = node.Operand as MemberExpression;

            if (memberExpression != null) {
                Name = memberExpression.Member.Name;
            }

            return base.VisitUnary(node);
        }

        protected override Expression VisitConstant(ConstantExpression node) {
            if (node.Value.GetType().Equals(typeof(string))) {
                Name = node.Value.ToString();
            }

            return base.VisitConstant(node);
        }
    }

    public static void Main(string[] args) {
        string str = "id";
        Expression<Func<Message, object>> exp1 = m => str;
        Expression<Func<Message, object>> exp2 = m => "id";
        Expression<Func<Message, object>> exp3 = m => m.ID;
        Expression<Func<Message, object>> exp4 = m => SomeMethod(m);
        var expressions = GetExpressions();


        var visitor = new NameResolverExpressionVisitor();

        visitor.Visit(exp1);
        Console.WriteLine(visitor.Name);
        visitor.Visit(exp2);
        Console.WriteLine(visitor.Name);
        visitor.Visit(exp3);
        Console.WriteLine(visitor.Name);
        visitor.Visit(exp4);
        Console.WriteLine(visitor.Name);

        foreach (var exp in expressions) {
            visitor.Visit(exp);
            Console.WriteLine(visitor.Name);
        }
    }
}