简体   繁体   English

如何使用反射来调节多个属性以检查 LINQ .Where 语句中的相等性,具体取决于传递的类?

[英]How use Reflection to condition multiple properties to check for equality in a LINQ .Where statement, depending on what class is passed?

I'm trying to generalize a duplicate checker function, which depending on which type of object, checks the properties said class has (provided in a configuration) are equal to those in another list.我试图概括一个重复的检查器函数,它取决于对象的类型,检查所述类具有的属性(在配置中提供)是否等于另一个列表中的属性。

I have decided to create a Dictionary, which will accept a type string for the key (Book, Author, Shop, etc.) and an array of properties that need to be equal.我决定创建一个字典,它将接受一个类型字符串作为键(书、作者、商店等)和一个需要相等的属性数组。

Example of Dictionary enties:字典条目示例:

"Book", ["Title", "CoverImage", "NumberOfPages"] 
"Author", ["Name", "Address", "SomethingElse"]

Then, I pass an object to the function and use Reflection to get the name of the type...然后,我将一个对象传递给函数并使用反射来获取类型的名称......

obj.GetType().Name;

... which I then use to fetch the right KVP from the Dictionary, meaning that if I pass a Book object, I get "Book". ...然后我用它从字典中获取正确的 KVP,这意味着如果我传递一个 Book 对象,我会得到“Book”。 We then use that to get the configuration via ...然后我们使用它来通过...获取配置

configDictionary["obj.GetType().Name"]

... which gives us the array of strings that are the properties that we need to check equality on. ...它为我们提供了字符串数组,这些字符串是我们需要检查相等性的属性。

I've gotten to the part where I need something along the lines of我已经到了我需要一些东西的部分

list.Where(x => --> for each of the strings in the array - x.GetType.GetProperty(string) && --> same for next string && same for next string

... and then I need to top it off with an... ......然后我需要用......

x.Id != obj.Id

To make sure we check for duplicates based on our logic (different id's and matches on all properties but has different Id's thus - a duplicate).确保我们根据我们的逻辑检查重复项(不同的 id 和所有属性的匹配,但因此具有不同的 Id - 重复)。

The end query should look like结束查询应如下所示

Books:图书:

someList.Where(x => 
x.Title == obj.Title 
&& x.CoverImage == obj.CoverImage 
&& x.NumberOfPages == obj.NumberOfPages 
&& x.Id != obj.Id)
.FirstOrDefault();

Authors:作者:

someList.Where(x => x.Name == obj.Name 
&& x.Address == obj.Address 
&& x.SomethingElse == obj.SomethingElse 
&& x.Id != obj.Id)FirstOrDefault();

Try to avoid reflection because it can slow down your application.尽量避免反射,因为它会减慢您的应用程序。 As an alternative you can create a dictionary and put all comparators into it:作为替代方案,您可以创建一个字典并将所有比较器放入其中:

var configDictionary = new Dictionary<string, List<Func<object, object, bool>>>
{
    {
        "Book",
        new List<Func<object, object, bool>>
        {
            (b1, b2) => ((Book)b1).Title == ((Book)b2).Title,
            (b1, b2) => ((Book)b1).CoverImage == ((Book)b2).CoverImage,
            (b1, b2) => ((Book)b1).NumberOfPages == ((Book)b2).NumberOfPages,
            (b1, b2) => ((Book)b1).Id != ((Book)b2).Id,
        }
    },
    // same for Authors
};

Now you can use it in Where method:现在您可以在Where方法中使用它:

var typeName = obj.GetType().Name; // here we using Reflection but once per collection, not per each item
var first = someList.Where(x => configDictionary[typeName].All(f => f(x, obj))).FirstOrDefault();

Also, because FirstOrDefault also has overload that accept predicate last line can be rewritten to:此外,因为FirstOrDefault也有接受谓词最后一行的重载可以重写为:

var first = someList.FirstOrDefault(x => configDictionary[typeName].All(f => f(x, obj)));

A better solution will be creating custom attribute which will tag property.更好的解决方案是创建将标记属性的自定义属性。 Then in class override default method Equals which will get all properties with this attribute and return equality.然后在类中覆盖默认方法 Equals,它将获取具有此属性的所有属性并返回相等性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM