给定单个对象过滤对象列表

时间:2018-10-23 19:34:04

标签: c# .net

我想要一个接受对象列表的方法,然后是“过滤器对象”(与对象列表类型相同)。我能够(低效率地)小规模地做到这一点,但也相当固定-我希望它是一种通用方法,因此我可以传入类型以便将其应用于任何事物。

示例:

public class Program {
    public void Main(string[] args) {
        var listOfObjects = new List<MyClass> {
            new MyClass { ID = 1, Name = "Object 1" },
            new MyClass { ID = 2, Name = "Object 2" },
            new MyClass { ID = 3, Name = "Object 2" }
        };
        var filter = new MyClass { Name = "Object 2" };

        // Should return only the first object in the list, since
        // the filter's Name = "Object 2", all items in the list
        // where the property equals "Object 2" will be filtered out
        var filteredList = FilterObjects(listOfObjects, filter);
    }
}

public class MyClass {
    public int ID { get; set; }
    public string Name { get; set; }
}

public class MyTest {
    public List<MyClass> FilterObjects(List<MyClass> objects, MyClass filter) {
        // first check if the filter is just an empty new instance
        // if a user passes in an empty filter then they are not
        // filtering anything, therefore 
        if (filter == new MyClass()) return objects;

        var filteredList = new List<MyClass>();
        foreach (var item in objects) {
            // if this item passes the test for the filter
            // (check if any of the properties are equal to the
            // filter properties - if so, then this item is not
            // is not a match, and we cannot add it to the list)
            if (item.ID != filter.ID && item.Name != filter.Name)
                filteredList.Add(item);
            // What I want is a faster and more consolidated way of
            // checking all the properties.
        }
        return filteredList;
    }
}

编辑:有什么方法也可以使用反射吗?

EDIT2:仅想澄清一下,我的示例只是一个简单的模板。我正在使用具有20多个属性的对象,并且希望在可能的情况下不必做出巨大的if语句。

EDIT3:我还要提及用户传递的过滤器对象可能不完整,例如它们可以传入没有ID的MyClass对象(只是Name属性),因为当到达我的Controller时,该MyClass对象将自动用默认值填充ID。我可以通过创建MyClass的新实例-new MyClass()来检查它是否为默认值,对于每个属性,如果它等于默认值,则可以忽略该属性以进行过滤,因为用户不希望这样属性已过滤。但是从更大的角度考虑这个概念,我拥有20多个属性,用户想要过滤掉所有对象,但只想使用其中的3个属性进行过滤。其他17个以上的属性将不进行相等性检查。

3 个答案:

答案 0 :(得分:2)

听起来像您想要的是通用语句。

这不是超级简单,但是类似这样的方法应该起作用:

public static IEnumerable<T> Filter<T>(this IEnumerable<T> results, Filter filter)
    {
        var types = results.GetType().GetProperties();

        foreach (var filter in filter.Filters)
        {
            Type type = results.GetType();
            filter.ColumnName = filter.ColumnName.Replace(" ", "");
            var pred = BuildPredicate<T>(filter.ColumnName, filter.FilterValue);
            if (filter.ColumnName != null && filter.FilterValue != null)
            {
                results = results.Where(w =>
                {                        
                    return w.GetType().GetProperty(filter.ColumnName).GetValue(w, null).ToString().ToLowerInvariant().Contains(filter.FilterValue.ToLowerInvariant());
                });
            }
        }

        return results;
    }

过滤器对象看起来像:

public class Filter
{
   public string ColumnName {get; set; }
   public string Value { get; set; }
   //Other properties for Asc / Desc and more
}

然后在诸如List或List之类的任何List上,您基本上都会这样做:

var results = MyList.Filter(new Filter() { ColumnName = "LastName"; Value = "Smith" });

将其转换为一个函数,如果手动键入该函数将类似于:

var results = MyList.Where(w => w.LastName == "Smith");

这个例子很粗糙,没有针对初学者的类型检查。

答案 1 :(得分:0)

为什么不仅仅使用System.Generic.Collections库中已经存在的方法。

 var filteredList= new List<MyClass>(listOfObjects);
 filteredList.RemoveWhere(n => n.Name == "Object 2"); 

如果要使用其他类作为过滤器:

MyClass filter = new MyClass() {Name = "Object 2", Id=2 };
var filteredList= new List<MyClass>(listOfObjects);
filteredList.RemoveWhere(n => (n.Name == filter.Name || n.Id == filter.Id)); // you can modify predicate based on whatever you wish to compare

答案 2 :(得分:0)

我将使用自定义的IsMatch方法:

static bool IsMatch (MyClass toTest, MyClass filter)
{
    if (filter.Prop1 != null // or whatever value means "skip this property"
        && filter.Prop1 == toTest.Prop1)
        return true;

    if (filter.Prop2 != null & filter.Prop2 == toTest.Prop2)
        return true;

    ...

    return false;
}

然后让Linq为您查找:

List<MyClass> filtered = listOfObjects.Where(x => !IsMatch(x, filter)).ToList();

单元测试(带有反射)确保此方法始终是最新的,现在和将来都要检查所有属性,这有助于在向类添加属性时不引入错误