如何根据通用属性过滤列表

时间:2019-05-08 14:01:19

标签: c# linq uwp

我正在尝试使用LINQ删除此处的硬编码。 基本上,我有10个属性,并且基于选定的属性,我想过滤列表。 有人可以帮我删除这种硬编码吗,有没有一种方法可以基于“ propertyName”字符串值获取属性的信息,并在过滤列表时使用该信息。 下面是参考代码 如果我的问题有道理,请告诉我。 预先感谢您的帮助。

if (propertyName == "Property1")
                {
                    FilteredList = CurrentList.Where(x => x.property1== propertyValue.ToString()).ToList();
                }
                else if (propertyName == "Property2")
                {
                    FilteredList = CurrentList.Where(x => x.property2== propertyValue.ToString()).ToList();
                }

2 个答案:

答案 0 :(得分:1)

a。反射(如果propertyName的值与实际属性名称匹配)

var t = typeof(...your type...);
var pi = t.GetProperty(propertyName);

CurrentFilterList = AfterFilterList
      .Where(x => pi.GetValue(x).ToString() == propertyValue)
      .ToList();

b。如果属性名称不匹配,则可以尝试以下方法:

var funcs = new Dictionary<string, Func<your_type, object>> 
{
   {"Company", v => v.Company},
   {"InspectionUnit", v => v.InspectionUnit}
};

var f = funcs[propertyName];
CurrentFilterList = AfterFilterList
   .Where(x => f(x) == propertyValue.ToString()).ToList();

代码可能不准确,但应该可以证明这一点。 #2应该有更好的性能。

BTW funcs也可以使用反射来动态构建-只需遍历所有道具并动态创建函数/表达式。

答案 1 :(得分:0)

这是一个无需反射即可创建Where条件的函数。仅出于说明目的,如果无法识别属性名称,它将返回一个始终返回true的条件-换句话说,没有过滤器。对于无法识别的输入,异常可能更有意义。

private Func<Thing, bool> GetCondition(string propertyName, string propertyValue)
{
    switch (propertyName)
    {
        case "Company":
            return thing => thing.Company == propertyValue;
        case "InspectionUnit":
            return thing => thing.InspectionUnit == propertyValue;
        default: return thing => true;
    }
}

这里是使用反射的版本。只要我们沿着这条路线走,就无需将属性限制为string值。只要实现了Equals,它就可以是任何东西,这对于字符串和值类型都是正确的。

private Func<Thing, bool> GetCondition<TSourceType, TPropertyType>
    (string propertyName, TPropertyType propertyValue)
{
    var property = typeof(TSourceType).GetProperty(
        propertyName, BindingFlags.Instance | BindingFlags.Public);
    if (property?.PropertyType != typeof(TPropertyType))
        return thing => true; // or throw an exception.
    return thing => property.GetValue(thing).Equals(propertyValue);
}

为了提高性能,将反射的属性存储在Dictionary<Type, Dictionary<string, PropertyInfo>>之类的位置将是有益的,这样对于任何给定的类型和属性名称,您都可以查找它,而不必重复反射。

在这两者之间,如果您只是在考虑少数可能的值并且正在处理已知类型,那么我会倾向于在您的方案中使用反射而不是

现在的用法是

非普通/非反射

var condition = GetCondition(propertyName, propertyValue);
var filtered = unfiltered.Where(condition);

一般/反思

var condition = GetCondition<Thing, string>(propertyName, propertyValue);
var filtered = unfiltered.Where(condition);

当我提到将属性存储在字典中时,就像这样:

private readonly Dictionary<Type, Dictionary<string, PropertyInfo>> _properties = new Dictionary<Type, Dictionary<string, PropertyInfo>>();

private PropertyInfo GetProperty(Type sourceType, string propertyName)
{
    if(!_properties.ContainsKey(sourceType))
        _properties.Add(sourceType, new Dictionary<string, PropertyInfo>());
    if (_properties[sourceType].ContainsKey(propertyName))
        return _properties[sourceType][propertyName];
    var property = sourceType.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public);
    _properties[sourceType].Add(propertyName, property);
    return property; // could be null;
}

但是,这又仅仅是一个例子。对于您所描述的场景来说,这太过分了。