接口的C#Expression.Property

时间:2019-01-09 13:55:07

标签: c# expression

我正在基于一些参数在CustomType上生成一个动态表达式。代码如下:

ParameterExpression parameter = Expression.Parameter(typeof(CustomType), "x");
MemberExpression idMember = Expression.Property(parameter, "CustomProperty");

当我从类型CustomType更改为接口ICustomType时,它因抛出错误'未为类型'ICustomType'定义实例属性'CustomProperty'而停止工作 。如何解决?

2 个答案:

答案 0 :(得分:1)

没有最小的可验证示例,我们不能确定问题出在哪里,但是从您的示例代码中,我得出以下结论:

interface ICustomType
{
    int CustomProperty { get; set; }
}

class CustomType : ICustomType
{
    public int CustomProperty { get; set; }
}

现在,当我调用您的示例代码时,一切都会按预期运行

ParameterExpression parameter = Expression.Parameter(typeof(CustomType), "x");
MemberExpression idMember = Expression.Property(parameter, "CustomProperty");

此外,当我将类型更改为ICustomType时,它仍然可以正常工作。

ParameterExpression parameter = Expression.Parameter(typeof(ICustomType), "x");
MemberExpression idMember = Expression.Property(parameter, "CustomProperty");

但是,如果我从CustomProperty中删除了ICustomType的声明,则会出现以下错误:

  

未为类型“ ICustomType”定义实例属性“ CustomProperty”

因此,这使我相信您的接口不包含CustomProperty的声明。如果将其添加到界面中,则您的代码应该可以正常工作。

答案 1 :(得分:0)

使用表达式时,常见的任务是将某些节点替换为其他节点。例如,您可以像在answer中那样替换ParameterExpression。我相信OP使用了类似的参数替换器,并且忘记了也替换了MemberExpression

如果替换表达式的参数,则原始MemberExpression可能不是新的参数类型。例如。 CustomType.CustomProperty的成员表达式将无法处理ICustomType.CustomProperty

如果我的理论正确,那么OP也必须替换某些MemberExpression实例。以下表达式访问者可以解决这个问题:

public class ParameterReplacerVisitor : ExpressionVisitor
{
    private readonly Type newType;
    private Dictionary<ParameterExpression, ParameterExpression> parametersToReplace;

    public ParameterReplacerVisitor(Type newType)
    {
        this.newType = newType;
    }

    public LambdaExpression Convert(LambdaExpression expression)
    {
        parametersToReplace = expression.Parameters
            .Where(p => ShouldReplace(p.Type))
            .ToDictionary(p => p, p => Expression.Parameter(newType, p.Name));

        return (LambdaExpression)Visit(expression);
    }

    protected override Expression VisitLambda<T>(Expression<T> node)
    {
        var parameters = node.Parameters.Select(GetNewParameter);
        return Expression.Lambda(Visit(node.Body), parameters);
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        return base.VisitParameter(GetNewParameter(node));
    }

    protected override Expression VisitMember(MemberExpression node)
    {
        if (ShouldReplace(node.Member.DeclaringType))
        {
            var targetProperty = GetNewProperty(node.Member);
            node = Expression.MakeMemberAccess(Visit(node.Expression), targetProperty);
        }

        return base.VisitMember(node);
    }

    private MemberInfo GetNewProperty(MemberInfo member)
    {
        return newType.GetProperty(member.Name) ?? throw new ArgumentException(
            $"Property '{member.Name}' is not defined for type '{newType.Name}'"
        );
    }

    private bool ShouldReplace(Type type) => newType.IsAssignableFrom(type);

    private ParameterExpression GetNewParameter(ParameterExpression parameter)
    {
        parametersToReplace.TryGetValue(parameter, out var newParameter);
        return newParameter ?? parameter;
    }
}

示例

Expression<Func<Derived, string>> derived = t => t.A;
var lessDerived = derived.ToLessDerived<Derived, IBase, string>();
var d = lessDerived.Compile();
var result = d.Invoke(new Base());

以及扩展方法:

public static Expression<Func<TLess, TValue>> ToLessDerived<TClass, TLess, TValue>(this Expression<Func<TClass, TValue>> expression)
{
    var visitor = new ParameterReplacerVisitor(typeof(TLess));
    return (Expression<Func<TLess, TValue>>)visitor.Convert(expression);
}

对我来说,这解决了OP要求的完全相同的错误类型。