【问题标题】:C# Expression.Property of an interface接口的 C# Expression.Property
【发布时间】:2019-01-09 13:55:07
【问题描述】:

我正在根据一些参数在 CustomType 上生成动态表达式。代码如下所示:

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

当我从 CustomType 类型更改为接口 ICustomType 时,它​​通过抛出错误“实例属性 'CustomProperty' 没有为类型 'ICustomType' 定义”而停止工作 .如何解决?

【问题讨论】:

  • 欢迎来到 Stack Overflow。您能否详细说明“它停止工作”的确切含义? minimal reproducible example 在这里会很有帮助。
  • 为什么参数是可枚举的?参数是列表对象是不寻常的。
  • @jdweng 我有 IQueryable,它代表对数据库的查询。
  • 我想我出错了,因为我使用的接口是 IQueryable customFunction(),而不是 IQueryable customFunction() where T:class, ICustomClass

标签: c# expression


【解决方案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");

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

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

因此,这使我相信您的界面不包含CustomProperty 的声明。如果你将它添加到你的界面,你的代码应该可以工作。

【讨论】:

    【解决方案2】:

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

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

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

    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 询问的完全相同类型的错误。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-17
      • 2011-12-22
      • 2011-04-20
      • 2011-12-09
      相关资源
      最近更新 更多