деконструкция метода callexpressions при передаче параметров в выражение - PullRequest
0 голосов
/ 19 апреля 2019

Я пытаюсь получить значение параметров, переданных в выражение. Система будет использовать фабрику сообщений об ошибках, передавая значения, полученные из родительского / вызывающего метода. Если я жестко закодирую значение и передам его в выражение, массив Method.Arguments будет иметь фактическое значение, а метод ниже извлечет это значение. Если он передается из родительского метода, он в итоге получит то, что похоже на представление сигнатуры вызова метода

.Constant<AutoValidator.Impl.Validator+<>c__DisplayClass7_0>(AutoValidator.Impl.Validator+<>c__DisplayClass7_0).minLength

Я не уверен, что я каким-то образом неправильно передаю значения или пытаюсь получить их действительное значение неправильно.

//the expression will receive the value 123
_errorMessageFactory.Get<string>((val, exp) => exp.MinLength(text, 123, message), propName);

//we pass xx which has the value of 123, but the expression doesn't show this value
var xx = 123;
_errorMessageFactory.Get<string>((val, exp) => exp.MinLength(text, xx, message), propName);
public Tuple<string, List<object>> Get<TMember>(Expression<Func<TMember, IValidatorExpression, bool>> exp, string propName)
{
            var methodCall = exp.Body as MethodCallExpression;
            var methodSignature = methodCall.Method.ToString();

GetArgumentValue(methodCall.Arguments[1]);
}

private object GetArgumentValue(Expression methodExpression)
{
    if (methodExpression.NodeType == ExpressionType.MemberAccess)
    {
        var memberExpression = (MemberExpression)methodExpression;
        return GetArgumentValue(memberExpression.Expression);
    }
    else if (methodExpression.NodeType == ExpressionType.Constant)
    {
        var constExp = methodExpression as ConstantExpression;
        return constExp?.Value;
     }
     throw new ArgumentOutOfRangeException("Unknown expression argument type");
}   

1 Ответ

1 голос
/ 19 апреля 2019

Вы можете реализовать свой собственный посетитель выражений, где вы будете переопределять VisitConstant для сбора информации о простых константах и ​​закрытых переменных в c__DisplayClass

private class ValueExtractor : ExpressionVisitor
{
    private readonly Dictionary<Type, Dictionary<string, object>> anonymousFields;

    public ValueExtractor()
    {
        Arguments = new List<object>();
        anonymousFields = new Dictionary<Type, Dictionary<string, object>>();
    }

    public List<object> Arguments { get; }

    protected override Expression VisitMember(MemberExpression node)
    {
        var memberName = node.Member.Name;
        var type = node.Member.DeclaringType;

        var baseResult = base.VisitMember(node);

        if (anonymousFields.ContainsKey(type))
            Arguments.Add(anonymousFields[type][memberName]);

        return baseResult;
    }

    protected override Expression VisitConstant(ConstantExpression node)
    {
        var constantType = node.Type;
        if (constantType == typeof(int) || constantType == typeof(string)) // and so on
        {
            Arguments.Add(node.Value);
        }
        else if (IsAnonymousType(constantType) && !anonymousFields.ContainsKey(constantType))
        {
            var fields = new Dictionary<string, object>();
            anonymousFields.Add(constantType, fields);

            foreach (var field in constantType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.GetField))
                fields.Add(field.Name, field.GetValue(node.Value));
        }

        return base.VisitConstant(node);
    }

    private static bool IsAnonymousType(Type type)
    {
        var hasSpecialChars = type.Name.Contains("<") || type.Name.Contains(">");
        return hasSpecialChars && type.GetCustomAttributes(typeof(CompilerGeneratedAttribute), inherit: false).Any();
    }
}

Использование:

public Tuple<string, List<object>> Get<TMember>(Expression<Func<TMember, IValidatorExpression, bool>> exp, string propName)
{
    var visitor = new ValueExtractor();
    visitor.Visit(exp);

    foreach (var argument in visitor.Arguments)
        Console.WriteLine(argument);
...