【问题标题】:reflection v expression反射 v 表达式
【发布时间】:2012-09-25 12:10:58
【问题描述】:

我正在查看 another question,它说明了 Expression 如何比反射快得多,因为它可以预编译为 IL。

虽然我不确定如何使用它。这是用于 Value Oject(在 DDD 意义上)的基类中使用的一些代码,其基本思想是使用所有公共属性的值来确定相等性,它通过反射获得。通过使用此基类,您无需为具有值对象的子类实现相等性。

protected virtual bool HasSameObjectSignatureAs(BaseObject compareTo) 
{
var signatureProperties = GetType().GetProperties();
foreach (var property in signatureProperties)
{
    var valueOfThisObject = property.GetValue(this, null);
    var valueOfCompareTo = property.GetValue(compareTo, null);

    if (valueOfThisObject == null && valueOfCompareTo == null) {
        continue;
    }

    if ((valueOfThisObject == null ^ valueOfCompareTo == null) ||
        (!valueOfThisObject.Equals(valueOfCompareTo))) {
            return false;
    }
}

如何使用 Expression 重写此代码?

干杯,
浆果

【问题讨论】:

  • 请为问题提供描述性标题。顺便说一句,它不是预编译的,它是后编译的,除非你可以缓存和重用代码。

标签: c# lambda expression-trees


【解决方案1】:

您可以通过为要比较的每个属性构建嵌套的And 表达式来做到这一点:

protected Expression<Func<BaseObject, bool>> CreatePropertiesEqualExpression(BaseObject other)
{
    if (! other.GetType().IsSubclassOf(this.GetType())) throw new ArgumentException();

    var properties = this.GetType().GetProperties();
    Expression trueExpr = Expression.Constant(true);
    Expression thisExpr = Expression.Constant(this);
    ParameterExpression paramExpr = Expression.Parameter(typeof(BaseObject), "compareTo");
    Expression downCastExpr = Expression.Convert(paramExpr, other.GetType());

    MethodInfo eqMethod = typeof(object).GetMethod("Equals", BindingFlags.Public | BindingFlags.Static);

    Expression propCompExpr = properties.Aggregate(trueExpr, (expr, prop) =>
    {
        Expression thisPropExpr = Expression.Property(thisExpr, prop);
        Expression compPropExpr = Expression.Property(downCastExpr, prop);
        Expression eqExpr = Expression.Call(null, eqMethod, Expression.Convert(thisPropExpr, typeof(object)), Expression.Convert(compPropExpr, typeof(object)));

        return Expression.And(expr, eqExpr);
    });

    return Expression.Lambda<Func<BaseObject, bool>>(propCompExpr, paramExpr);
}

然后你可以像这样使用它:

public class SubObject : BaseObject
{
    public int Id { get; set; }
    public string Name { get; set; }
    private Func<BaseObject, bool> eqFunc;

    public bool IsEqualTo(SubObject other)
    {
        if(this.eqFunc == null)
        {
            var compExpr = this.CreatePropertiesEqualExpression(other);
            this.eqFunc = compExpr.Compile();
        }
        return this.eqFunc(other);
    }
}

【讨论】:

  • 即使没有缓存,反射性能会更好吗?什么时候需要使缓存失效?你能用缓存显示代码吗?干杯
  • @Berryl - 我已更新代码以缓存比较委托。每次创建和编译表达式都会比您当前的方法慢,但如果您在单个实例上多次调用此方法,从长远来看,使用已编译的委托可能会更快。我不知道您是如何使用此方法的,但如果您为每个 BaseObject 类型创建一个静态委托,并将比较类型更改为 Func&lt;T, T, bool&gt;,其中 T 是特定的子类型,效率会更高。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-12
  • 1970-01-01
相关资源
最近更新 更多