How use Reflection to condition multiple propertie

2019-07-11 04:15发布

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". 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).

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();

2条回答
Bombasti
2楼-- · 2019-07-11 04:40

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.

查看更多
迷人小祖宗
3楼-- · 2019-07-11 04:47

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:

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:

var first = someList.FirstOrDefault(x => configDictionary[typeName].All(f => f(x, obj)));
查看更多
登录 后发表回答