在没有实例对象的情况下,是否可以从表达式的泛型参数中获取属性值?

问题描述 投票:0回答:1

假设我有以下扩展方法和支持类,并用类似的方式调用它:

AppLogs = await Context.PageAsync<AppLog>(log => log.IsActive == true && log.Text == "foo");

PageAync 方法不相关,但它最终会调用 GetMenebers:

public static IReadOnlyList<KeyValuePair<T, object?>> GetMembers<T>(this Expression<Func<T, object?>> expression) where T : class, new()
    {
        var visitor = new PropertyVisitor<T>();
        
        visitor.Visit(expression.Body);
        visitor.Path.Reverse();
        
        return visitor.Path;
    }


internal class PropertyVisitor<T> : ExpressionVisitor
{
    internal readonly List<KeyValuePair<T, object?>> Path = new();

    protected override Expression VisitMember(MemberExpression node)
    {
        if (!(node.Member is PropertyInfo info))
            throw new ArgumentException("The path can only contain properties", nameof(node));
        

        Path.Add(new KeyValuePair<T, object>(((PropertyInfo)member).Name, ((PropertyInfo)member).GetValue(***NEED OBJ***))));
        return base.VisitMember(node);
    }
}

public class AppLog()
{
    public string Text { get; set; }

    public bool IsActive { get; set; }
}

访问者所需的结果。路径将是两个键值对:

Key: "IsActive", Value: true

Key: "Text", Value: "Foo"

由于调用本身没有实例对象,通过类型推断参数,有没有办法获取这两个属性值?

我假设如果有一个方法,它不会是通过使用PropertyInfo。但我不知道另一种反思方式。

本质上,我需要的几乎正是此处所看到的内容,但需要的是值,而不是名称。

更新

我认为修改我的访问者应该可行。很快就会更新。

internal class PropertyVisitor<T> : ExpressionVisitor
{
    internal readonly List<KeyValuePair<T, object?>> Path = new();

    protected override Expression VisitMember(MemberExpression node)
    {
        // if (!(node.Member is PropertyInfo info))
        //     throw new ArgumentException("The path can only contain properties", nameof(node));
        

        Path.AddRange(ExtractConstants(node));
        return base.VisitMember(node);
    }
    
    private static IEnumerable<KeyValuePair<string, object?>> ExtractConstants(MemberExpression memberExpression)
    {
        var constants = new List<KeyValuePair<string, object?>>();
        var constExpression = (ConstantExpression)memberExpression.Expression;
        var valIsConstant = constExpression != null;
        Type declaringType = memberExpression.Member.DeclaringType;
        object declaringObject = memberExpression.Member.DeclaringType;

        if (valIsConstant)
        {
            declaringType = constExpression.Type;
            declaringObject = constExpression.Value;
        }

        var member = declaringType.GetMember(memberExpression.Member.Name, MemberTypes.Field | MemberTypes.Property, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static).Single();

        if (member.MemberType == MemberTypes.Field)
            constants.Add(new KeyValuePair<string, object>(((FieldInfo)member).Name, ((FieldInfo)member).GetValue(declaringObject)));
        else
            constants.Add(new KeyValuePair<string, object>(((FieldInfo)member).Name, ((PropertyInfo)member).GetGetMethod(true).Invoke(declaringObject, null)));

        return constants;
    }
}
c# generics reflection expression
1个回答
0
投票

在表达式

log => log.IsActive == true && log.Text == "foo"
等式 (
==
) 的示例中,是一个 BinaryExpression,所以让我们在访问者中捕获二进制表达式:

internal class PropertyVisitor<T> : ExpressionVisitor
{
    internal readonly List<KeyValuePair<string, object>> Path = new();

    protected override Expression VisitBinary(BinaryExpression node)
    {
        if(node.NodeType == ExpressionType.Equal) {
            ConstantExpression constantExpression = null;
            MemberExpression memberExpression = null;
            if(node.Right is ConstantExpression right) {
                constantExpression = right;
                memberExpression = (MemberExpression)node.Left;
            } else if(node.Left is ConstantExpression left) {
                constantExpression = left;
                memberExpression = (MemberExpression)node.Right;
            }

            if(constantExpression == null)
                throw new ArgumentException("Only equality to constants supported", nameof(node));
            if(memberExpression == null)
                throw new ArgumentException("The path can only contain properties", nameof(node));

            string name = ((MemberInfo)memberExpression.Member).Name;
            Object @value = constantExpression.Value;
            Path.Add(new KeyValuePair<string, object>(name, @value));
        }
        return base.VisitBinary(node);
    }
}

然后称为

var result = GetMembers<AppLog>(log => log.IsActive == true && log.Text == "foo");
Console.WriteLine(String.Join(Environment.NewLine, result.Select(kv => $"{kv.Key} = {kv.Value}")));

它产生输出:

Text = foo
IsActive = True
© www.soinside.com 2019 - 2024. All rights reserved.