C# Linq 筛选器 IEnumerable 动态属性和值

本文关键字:属性 动态 IEnumerable Linq 筛选 | 更新日期: 2023-09-27 17:57:05

public class Sample
{
    public int Id { get; set; }
    public string Description { get; set; }
    public DateTime EffectiveDate { get; set; }
}
IEnumerable<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 作为"描述"传递,但是当"Id"作为 filterColumn 和一些 filterValue (如"1")传递时,字符串和 int 错误之间会抛出不兼容的"="运算符。

感谢任何帮助。谢谢

C# Linq 筛选器 IEnumerable 动态属性和值

您的第一种方法可以奏效。扩展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)
   );
}
IEnumerable<Sample> sampleList;
var byId = GetFiltered(sampleList, "Id", 100);
var byDescription = GetFiltered(sampleList, "Description", "Some Value");

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

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)

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

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

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

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

这样做的优点是可以执行更复杂的比较,例如按值范围或包含多个属性的值范围添加自定义筛选器。