C#Linq过滤IEnumerable的动态属性和值

时间:2016-07-14 21:40:27

标签: c# linq filter

public class Sample
{
public int Id { get; set; }
public string Description { get; set; }
public DateTime EffectiveDate { get; set; }
}

IEnumberable<Sample> sampleList;
//Populate the list

现在我想通过“Id”属性,有时是“描述”属性来过滤列表。只想将属性名称(filterColumn)和属性值(filterValue)作为字符串传递。

我尝试了以下内容:

IEnumerable<Sample> result = sampleList.Where(x => x.GetType().GetProperty(filterColumn).Name == filterValue);

string whereQuery = string.Format(" {0} = \"{1}\"", filterColumn, filterValue);
IEnumerable<Sample> result = sampleList.AsQueryable().Where(whereQuery);

如果我将filterColumn作为“Description”传递,则第二个选项有效,但当“Id”作为filterColumn传递并且某些filterValue类似于“1”时,会在string和int错误之间抛出不可编译的'='运算符。

感谢任何帮助。感谢

2 个答案:

答案 0 :(得分:2)

您的第一种方法可行。扩展Jon Skeet的评论,这是调整后的陈述。

IEnumerable<Sample> result = sampleList.Where(
  x => x.GetType().GetProperty(filterColumn).GetValue(x, null).Equals(filterValue)
);

要围绕此设置一些上下文,您必须允许不同的数据类型。您可以通过至少两种方式执行此操作:使用泛型方法或使用对象数据类型。为了便于说明,我将使用对象方法。

public IEnumerable<Sample> GetFiltered(
  IEnumerable<Sample> samples, string filtercolumn, object filtervalue
{ 
   return samples.Where(
      x => x.GetType().GetProperty(filtercolumn).GetValue(x, null).Equals(filtervalue)
   );
}

IEnumberable<Sample> sampleList;

var byId = GetFiltered(sampleList, "Id", 100);
var byDescription = GetFiltered(sampleList, "Description", "Some Value");

这个例子不是很安全,因为没有类型检查来确保属性值与你传入的数据类型相同。例如,没有什么能阻止你传递&#34;描述&# 34;和100作为参数。您无法在整数和字符串之间进行有意义的比较,因此您总是会得到一个空结果。 Equals方法不会抛出异常,只是看到两个对象不同。正如Jon指出的那样,在这种情况下,你总是希望使用Equals而不是&#34; ==&#34;运营商。 Equals方法用于比较内容,而#34; ==&#34;比较参考。例如:

Console.WriteLine(12 == 12); 
// True

object a = 12;
object b = 12;

Console.WriteLine(a == b); 
// False - because, due to boxing, a and b are separate objects
// that happen to contain the same value. (Check out "boxing" 
// if this doesn't make sense.)

Console.WriteLine(a.Equals(b)); 
// True - because the Equals method compares content (value)

另外,请注意,在使用&#34; ==&#34;时,字符串会有一些特殊的行为。运营商。要记住的重要一点是引用(容器)和内容之间存在差异。您想要比较内容,这意味着等于。 (我注意到Visual Studio中的立即窗口在使用&#34; ==&#34;时对字符串的结果不一致。我怀疑这是因为字符串引用可以但不总是在该窗口中进行优化。)

您声明您的第二种方法有效。我没有在标准的IEnumerable.Where方法中看到过这种类型的过滤字符串。所以我猜你正在使用一些扩展。您的示例无法正常显示。 DataTable类使用与您的用法匹配的筛选字符串。通常,必须基于数据类型以不同方式构造过滤器字符串。例如,字符串需要引号(您有)但整数值不使用引号。

答案 1 :(得分:1)

您拥有的另一个选项是设置包含所需操作的字典。

public IEnumerable<Sample> GetFiltered(
    IEnumerable<Sample> samples, string property, string value)
{
    var map = new Dictionary<string, Func<string, Func<Sample, bool>>>()
    {
        { "Description", v => s => s.Description == v },
        { "Id", v => s => s.Id == int.Parse(v) },
    };
    return samples.Where(map[property](value));
}

此处的优点是您可以执行更复杂的比较,例如按值范围添加自定义过滤器,或者包含多个属性的过滤器。