C# 使用表达式树通过单个属性比较对象无效操作异常

C# 使用表达式树通过单个属性比较对象无效操作异常,c#,expression-trees,C#,Expression Trees,我尝试使用表达式树,因为基于描述,这似乎是最正确的性能、可配置的方法 我希望能够创建一个语句,从existingItems集合中获取与incomingItem的propertyNameToCompareOn值匹配的第一个项 我有一个具有以下签名和模拟代码体的方法 DetectDifferences<T>(List<T> incomingItems, List<T> existingItems) { var propertyNameToCompareOn =

我尝试使用表达式树,因为基于描述,这似乎是最正确的性能、可配置的方法

我希望能够创建一个语句,从existingItems集合中获取与incomingItem的propertyNameToCompareOn值匹配的第一个项

我有一个具有以下签名和模拟代码体的方法

DetectDifferences<T>(List<T> incomingItems, List<T> existingItems)
{
  var propertyNameToCompareOn = GetThisValueFromAConfigFile(T.FullName());

  //does this belong outside of the loop?
  var leftParam = Expression.Parameter(typeof(T), "left");
  var leftProperty = Expression.Property(leftParam, identField);
  var rightParam = Expression.Parameter(typeof(T), "right");
  var rightProperty = Expression.Property(rightParam, identField);
  //this throws the error
  var condition = Expression.Lambda<Func<T, bool>>(Expression.Equal(leftProperty, rightProperty));

  foreach (var incomingItem in incomingItems) //could be a parallel or something else. 
  {
     // also, where am I supposed to provide incomingItem to this statement?
     var existingItem = existingItems.FirstOrDefault(expression/condition/idk);

     // the statement for Foo would be something like
     var existingFoos = exsistingItems.FirstOrDefault(f => f.Bar.Equals(incomingItem.Bar);

     //if item does not exist, consider it new for persistence
     //if item does exist, compare a configured list of the remaining properties between the 
     // objects. If they are all the same, report no changes. If any
     // important property is different, capture the differences for 
     // persistence. (This is where precalculating hashes seems like the 
     // wrong approach due to expense.)
  }
}
在上面标记的行中,我得到了为lambda声明invalidoOperationException提供的参数数量不正确。在这一点上,我只是从网络上黑客一起废话,我真的不知道这是想要什么。VS可以在我的屏幕上显示大量重载,而MSDN/SO上的文章中没有一个例子是有意义的


PS-如果可以帮助的话,我真的不想要一个IComparer或类似的实现。我可以通过反思来做到这一点。我确实需要尽可能快地实现这一点,但允许对多个类型调用它,因此可以选择表达式树。

在使用表达式树时,首先在实际代码中理解您想要做什么很重要

我总是首先在静态代码中写出实数C lambda语法的结果表达式

根据您的描述,您声明的目标是,您应该能够动态查找T类型的某些属性,以提供某种快速比较。如果T和TProperty在编译时都是已知的,您将如何编写这篇文章? 我怀疑它看起来会像这样:

Func<Foo, Foo, bool> comparer = (Foo first, Foo second) => 
    first.FooProperty == second.FooProperty;
这只是使用Expression.Lambda的重载。每个ParameterExpression都是主体中使用的参数。就这样。别忘了。如果你真的想调用它,就把你的表达式编译成一个委托


当然,这并不意味着你的技术一定很快。如果您使用奇特的表达式树来比较两个列表,并采用简单的“^2”方法,这并不重要。

在使用表达式树时,首先在实际代码中理解您想要做什么很重要

我总是首先在静态代码中写出实数C lambda语法的结果表达式

根据您的描述,您声明的目标是,您应该能够动态查找T类型的某些属性,以提供某种快速比较。如果T和TProperty在编译时都是已知的,您将如何编写这篇文章? 我怀疑它看起来会像这样:

Func<Foo, Foo, bool> comparer = (Foo first, Foo second) => 
    first.FooProperty == second.FooProperty;
这只是使用Expression.Lambda的重载。每个ParameterExpression都是主体中使用的参数。就这样。别忘了。如果你真的想调用它,就把你的表达式编译成一个委托


当然,这并不意味着你的技术一定很快。如果您使用奇特的表达式树来比较两个列表,并采用简单的^2方法,那就没关系了。

这里有一个创建属性访问表达式的方法

    public static Expression<Func<T, object>> MakeLambda<T>(string propertyName)
    {
        var param = Expression.Parameter(typeof(T));
        var propertyInfo = typeof(T).GetProperty(propertyName);
        var expr = Expression.MakeMemberAccess(param, propertyInfo);
        var lambda = Expression.Lambda<Func<T, object>>(expr, param);
        return lambda;
    } 
请注意==仅适用于int等值类型;小心比较对象

这证明了lambda方法要快得多

    static void Main(string[] args)
    {
        var l1 = new List<Foo> { };
        for(var i = 0; i < 10000000; i++)
        {
            l1.Add(new Foo { Name = "x" + i.ToString() });
        }

        var propertyName = nameof(Foo.Name);
        var lambda = MakeLambda<Foo>(propertyName);
        var f = lambda.Compile();

        var propertyInfo = typeof(Foo).GetProperty(nameof(Foo.Name));

        var sw1 = Stopwatch.StartNew();
        foreach (var item in l1)
        {
            var value = f(item);
        }
        sw1.Stop();


        var sw2 = Stopwatch.StartNew();
        foreach (var item in l1)
        {
            var value = propertyInfo.GetValue(item);
        }
        sw2.Stop();

        Console.WriteLine($"{sw1.ElapsedMilliseconds} vs {sw2.ElapsedMilliseconds}");



    }

不过,正如有人指出的,OP中的双循环在^2上,如果效率是这里的驱动因素,那么这可能是下一个考虑因素。

这里有一个创建属性访问表达式的方法

    public static Expression<Func<T, object>> MakeLambda<T>(string propertyName)
    {
        var param = Expression.Parameter(typeof(T));
        var propertyInfo = typeof(T).GetProperty(propertyName);
        var expr = Expression.MakeMemberAccess(param, propertyInfo);
        var lambda = Expression.Lambda<Func<T, object>>(expr, param);
        return lambda;
    } 
请注意==仅适用于int等值类型;小心比较对象

这证明了lambda方法要快得多

    static void Main(string[] args)
    {
        var l1 = new List<Foo> { };
        for(var i = 0; i < 10000000; i++)
        {
            l1.Add(new Foo { Name = "x" + i.ToString() });
        }

        var propertyName = nameof(Foo.Name);
        var lambda = MakeLambda<Foo>(propertyName);
        var f = lambda.Compile();

        var propertyInfo = typeof(Foo).GetProperty(nameof(Foo.Name));

        var sw1 = Stopwatch.StartNew();
        foreach (var item in l1)
        {
            var value = f(item);
        }
        sw1.Stop();


        var sw2 = Stopwatch.StartNew();
        foreach (var item in l1)
        {
            var value = propertyInfo.GetValue(item);
        }
        sw2.Stop();

        Console.WriteLine($"{sw1.ElapsedMilliseconds} vs {sw2.ElapsedMilliseconds}");



    }

不过,正如有人指出的那样,OP中的双循环在^2上,如果效率是这里的驱动因素,那么这可能是下一个考虑因素。

如果您关心性能,请从修复实际算法开始。使用基于散列的数据结构来进行搜索,而不是在列表上对另一个列表中的每个项目进行线性搜索。这对我描述的表达式树问题有何帮助?您试图解决一个问题,而忽略了另一个更重要的问题。在你花时间进行微优化之前,先修复你的宏优化。@Servy-当对象存在时,没有办法进行后续比较,因为它们通常比这更激烈-每个项目的其余属性。提前计算两个集合中每个项的每个属性的哈希似乎不会更快,除非您是指其他内容。@StingyJack使用PropertyInfo.GetValue和表达式树版本之间的测量差异是什么?我怀疑你是否会看到显著的不同;你有数字吗?好吧,如果你关心性能,从修正实际算法开始。使用基于散列的数据结构来进行搜索,而不是在列表上对另一个列表中的每个项目进行线性搜索。这对我描述的表达式树问题有何帮助?您正在尝试
解决一个问题而忽视另一个问题更为重要。在你花时间进行微优化之前,先修复你的宏优化。@Servy-当对象存在时,没有办法进行后续比较,因为它们通常比这更激烈-每个项目的其余属性。提前计算两个集合中每个项的每个属性的哈希似乎不会更快,除非您是指其他内容。@StingyJack使用PropertyInfo.GetValue和表达式树版本之间的测量差异是什么?我怀疑你是否会看到显著的不同;你有电话号码吗?谢谢,这个有用。我认为我的问题的根源是我试图将PropertyGet和该属性的相等性比较合并到一个lambda表达式中,但不理解它需要两步。我认为你可能会达到这一点,但现在你至少有了一些更容易合并的部分!谢谢,这个有用。我认为我的问题的根源是我试图将PropertyGet和该属性的相等性比较合并到一个lambda表达式中,但不理解它需要两步。我认为你可能会达到这一点,但现在你至少有了一些更容易合并的部分!没有这项工作,我甚至无法得到比较的时间。你关于写出来的第一点建议很好。我想我是想同时做两件事,而不是把它们分开。如果没有这项工作,我甚至无法得到比较的时间。你关于写出来的第一点建议很好。我想我是想同时做两件事,而不是把它们分开。