【问题标题】:Cannot convert from Expression<Func<T,bool>> to Expression<Func<User,bool>> when T = User within class当 T = 类内的用户时,无法从 Expression<Func<T,bool>> 转换为 Expression<Func<User,bool>>
【发布时间】:2020-10-16 04:29:00
【问题描述】:

假设我有这个类和方法,Store.Users 的上下文。 Get 的类型为 User。但是,当我将方法参数设置为Expression&lt;Func&lt;T,bool&gt;&gt; 并将其作为Get() 方法的表达式传递时。我收到一个 IDE & build 错误说明:

Argument 1 : cannot convert from Expression<Func<T,bool>> to Expression<Func<User,bool>>.
'Argument Type: Expression<Func<T,bool>> is not assignable to parameter type Expression<Func<User,bool>>'.

除了本例之外,代码的所有其他部分都能够识别出T 的类型为User。任何提示/想法都会很棒。

public class Builder<T> : DataBuilder<T> where T : User
{
   public override T Exists(Expression<Func<T, bool>> predicateExpression)
    {
       return Store.Users.Get(predicateExpression).FirstOrDefault();            
    } 
}

【问题讨论】:

  • Store.Users.Get 的类型是严格的,即它需要是用户。使用您拥有的代码,它可以是其他东西,因此编译器不会允许它。泛型 where 约束告诉编译器它可以是 User 或继承自 User 的类。假设您创建了从 User 继承的 SuperUser,您将无法使用 Expression> 调用 Store.Users.Get。不过,你所做的很有趣。祝你好运!
  • 为什么Builder 是通用的?为什么没有class Builder : DataBuilder&lt;User&gt;
  • 您可以实现更通用的访问,而不是调用 Store.Users.Get,例如在 context.Set().Get() 上?

标签: c# linq generics type-conversion


【解决方案1】:

另一种选择是修改 Expression 以使用 as 转换为正确的类型 - 但是,传入错误的类型会导致运行时异常,并且这样做会产生一些轻微的开销。

首先,您需要一个 ExpressionVisitor 来替换 Expression 中的 Expressions(感觉应该是内置的):

public static class ExpressionExt {
    /// <summary>
    /// Replaces an Expression (reference Equals) with another Expression
    /// </summary>
    /// <param name="orig">The original Expression.</param>
    /// <param name="from">The from Expression.</param>
    /// <param name="to">The to Expression.</param>
    /// <returns>Expression with all occurrences of from replaced with to</returns>
    public static Expression Replace(this Expression orig, Expression from, Expression to) => new ReplaceVisitor(from, to).Visit(orig);

    /// <summary>
    /// ExpressionVisitor to replace an Expression (that is Equals) with another Expression.
    /// </summary>
    public class ReplaceVisitor : ExpressionVisitor {
        readonly Expression from;
        readonly Expression to;

        public ReplaceVisitor(Expression from, Expression to) {
            this.from = from;
            this.to = to;
        }

        public override Expression Visit(Expression node) => node == from ? to : base.Visit(node);
    }
}

然后你可以使用它来用适当的as 包装原始的Expression 参数:

public class Builder<T> : DataBuilder<T> where T : User {
    public override T Exists(Expression<Func<T, bool>> predicateExpression) {
        var p = predicateExpression.Parameters[0];
        var pAsUser = Expression.MakeUnary(ExpressionType.TypeAs, p, typeof(User));
        var b = predicateExpression.Body.Replace(p, pAsUser);
        var newpe = Expression.Lambda<Func<User, bool>>(b, p);

        return (T)Store.Users.Get(newpe).FirstOrDefault();
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-17
    • 1970-01-01
    • 2023-03-31
    相关资源
    最近更新 更多