c# - 获取完整类属性 "Tree"名称作为字符串

标签 c# .net properties linq-expressions

我正在尝试做的事情可能有点奇怪。 但是我正在尝试(这是我能解释的最好的方法)使用反射来获取完整的类属性树名称作为字符串。


到目前为止的成功例子:

通过使用表达式,我可以转换:

() => Model.Cargo.Id

进入字符串:

"Model.Cargo.Id"

我现在的问题是,当我在组合中使用数组时,我没有得到数组名称。我得到的只是最后一个属性名称。

不成功的例子:

Model.CargoTasks[j].IsSet

只返回字符串:

"IsSet"

理想情况下我想要以下字符串结果:

"Model.CargoTasks[0].IsSet"

为了将索引包含在结果中,我可能要求有点高,但如果这可能的话,那就太棒了。


我正在使用处理这些示例的代码如下:

public static string ToMemberAccess<TResult>(this Expression<Func<TResult>> expression)
{
    // Get the body of the expression
    Expression body = expression.Body;
    if (body.NodeType != ExpressionType.MemberAccess && body.NodeType != ExpressionType.Convert)
    {
        throw new ArgumentException("Property expression must be of the form '() => SomeProperty'", "expression");
    }

    var memberExpression = expression.Body as MemberExpression ?? ((UnaryExpression)expression.Body).Operand as MemberExpression;
    var stuff = GetMemberNames(memberExpression);
    stuff.Reverse();

    return string.Join(".", stuff);
}

static List<string> GetMemberNames(MemberExpression expression,  List<string> actual = null)
{
    if (actual == null) actual = new List<string>();

    var member = expression.Member;
    var subExp = expression.Expression as MemberExpression;
    actual.Add(member.Name);

    if(subExp != null) actual = GetMemberNames(subExp, actual);

    return actual;
}

提前致谢!任何帮助将不胜感激!

最佳答案

要获取索引器中的值,您必须编译并执行表达式 - 这成本过高,但可以使用 ExpressionStringBuilder 的修改版本来完成。请注意,我添加了一个参数 compileConstants。当它设置为 false 时,输出将类似于 Model.CargoTasks[_.j].IsSet

请注意,此示例访问者是不完整的(即它不支持所有类型的表达式)。您可以使用 code in GitHub 对其进行补充.

public static string ToMemberAccess<TResult>(Expression<Func<TResult>> expression, bool compileConstants = false)
{
    var builder = new ExpressionStringBuilder(compileConstants);
    builder.Visit(expression);
    return builder.ToString();
}

internal class ExpressionStringBuilder : ExpressionVisitor
{
    private readonly bool _compileConstants;
    private readonly StringBuilder _out;

    public ExpressionStringBuilder(bool compileConstants)
    {
        _compileConstants = compileConstants;
        _out = new StringBuilder();
    }

    protected override Expression VisitConstant(ConstantExpression node)
    {
        if (node.Value != null)
        {
            string text = node.Value.ToString();
            if (node.Value is string)
            {
                Out("\"");
                Out(text);
                Out("\"");
            }
            else if (text == node.Value.GetType().ToString())
            {
                Out('_');
            }
            else
            {
                Out(text);
            }
        }
        else
        {
            Out("null");
        }
        return node;
    }

    protected override Expression VisitMethodCall(MethodCallExpression node)
    {
        int num = 0;
        Expression expression = node.Object;
        if (Attribute.GetCustomAttribute(node.Method, typeof(ExtensionAttribute)) != null)
        {
            num = 1;
            expression = node.Arguments[0];
        }
        var name = node.Method.Name;
        var isIndexer = name == "get_Item";
        if (expression != null)
        {
            Visit(expression);
            if (!isIndexer)
            {
                Out('.');
            }
        }
        if (isIndexer)
            Out('[');
        else
        {
            Out(name);
            Out('(');
        }
        int i = num;
        int count = node.Arguments.Count;
        while (i < count)
        {
            if (i > num)
            {
                Out(", ");
            }
            VisitArgument(node.Arguments[i]);
            i++;
        }
        Out(isIndexer ? ']' : ')');
        return node;
    }

    protected override Expression VisitIndex(IndexExpression node)
    {
        if (node.Object != null)
        {
            Visit(node.Object);
        }
        else
        {
            Out(node.Indexer.DeclaringType.Name);
        }
        if (node.Indexer != null)
        {
            Out(".");
            Out(node.Indexer.Name);
        }

        Out('[');
        for (var index = 0; index < node.Arguments.Count; index++)
        {
            if (index > 0)
            {
                Out(", ");
            }
            var expression = node.Arguments[index];
            VisitArgument(expression);
        }
        Out(']');
        return node;
    }

    protected override Expression VisitLambda<T>(Expression<T> node)
    {
        Visit(node.Body);
        return node;
    }

    protected override Expression VisitMember(MemberExpression node)
    {
        OutMember(node.Expression, node.Member);
        return node;
    }

    public override string ToString()
    {
        return _out.ToString();
    }

    private void VisitArgument(Expression expression)
    {
        if (_compileConstants)
        {
            // TODO: possibly check the expression is not dependent on parameters
            var value = Expression.Lambda(expression).Compile().DynamicInvoke();
            Out(value + string.Empty);

        }
        else
        {
            VisitArgument(expression);
        }
    }

    private void OutMember(Expression instance, MemberInfo member)
    {
        if (instance != null)
        {
            Visit(instance);
            if (_out.Length > 0)
                Out('.');
            Out(member.Name);
            return;
        }
        Out(member.DeclaringType.Name + "." + member.Name);
    }

    private void Out(char c)
    {
        _out.Append(c);
    }

    private void Out(string s)
    {
        _out.Append(s);
    }
}

关于c# - 获取完整类属性 "Tree"名称作为字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38258381/

相关文章:

c# - 如何为User32.dll的sendInput方法在mouse_input中设置适当的缩放值?

c# - 与 Specflow 并行运行 BDD 测试时,如何在 C# 中创建范围报告

spring - mule 3.3 自动重新加载属性

c# - 限制属性可接受的值范围的正确方法是什么?

spring - 在其他 Spring 消息(属性文件)中使用一个消息作为参数

c# - x509certificate2 中的空私钥

具有多个输出到服务总线的 C# Azure 函数

.net - 我的 C# .NET 团队是否应该迁移到 Windows Presentation Foundation?

java - .Net 和 Java 应用程序之间的单点登录

c# - 用于 .NET 的 Python