如何在泛型方法中过滤集合

How to filter a collection inside a generic method

我有两个 class 具有以下属性

 Class A
  {
      public int CustID { get; set; }
      public bool isProcessed { get; set; }
  }
  Class B
  {
      public int EmpId{ get; set; }
      public bool isProcessed { get; set; }
  }

我创建了一个接受所有这些 classes 的通用方法。'isProcessed' 属性 在这两个 classes 中都很常见。

public void ProceesData<T>(IList<T> param1, string date1)
{

}

我需要以下东西

  1. 在 ProcessData 方法中,我想过滤具有 isProcessed 标志是 "True".
  2. 另外我想迭代这个集合并需要为 IsProcessed 设置值 属性。

注意:我更喜欢使用反射的解决方案,因为 属性 名称是常量(即 "IsProcessed")

谁能帮忙解决这个问题。

创建一个接口,如 IProcessData,其中包含布尔值 属性、IsProcessed。让两个 类 实现这个接口。更改您的 ProcessData 方法,使其不再具有通用符号 (<T>) 并接受 IList<IProcessData>。然后对 param1 数据执行过滤和迭代。

最简单的方法是确保 类 实现一个公共接口并限制您的通用方法。例如:

public interface IProcessable
{
    bool isProcessed { get; set; }
}
public class A : IProcessable
{
    public int CustID { get; set; }
    public bool isProcessed { get; set; }
}

public class B : IProcessable
{
    public int EmpId { get; set; }
    public bool isProcessed { get; set; }
}

现在您的方法将如下所示:

public void ProceesData<T>(IList<T> param1, string date1)
    where T : IProcessable // <-- generic constraint added
{
    foreach (var element in param1)
    {
        element.isProcessed = true;
    }
}

如果您不能使用接口或 属性 名称不同,另一个更有用的选项是将 Action<T> 作为参数传递给您的方法。例如:

public void ProceesData<T>(IList<T> param1, string date1, Action<T> func)
{
    foreach (var element in param1)
    {
        func(element);
    }
}

并这样称呼它:

ProceesData<A>(list, "", x => x.isProcessed = true);

Note: I am preferring solution using reflection

此方法将迭代 collection,根据 propertyNamefilterValue 进行过滤,并使用反射将值设置为 newValue

public void ProceesData<T>(IList<T> param1, string date1, string propertyName, 
                                  object filterValue, object newValue)
{
    PropertyInfo pi = typeof(T).GetProperty(propertyName);

    object value;

    for (int i = param1.Count; i <= 0; i--)
    {
        value = pi.GetValue(param1[i]);
        if (value.Equals(filterValue))
        {
            pi.SetValue(param1[i], newValue);
        }
    }
}

你可以这样称呼它:

ProceesData<A>(a_list, "", "isProcessed", false, true);

免责声明:

虽然这是可能的。离得救还很远。如果你交错 属性 名字,它将失败!我建议通过@DavidG 移交 Action 委托来使用第二种方法。这样会让整个处理过程更加完善,不容易出错。我建议在这里使用正常的反向 for-loop,因为这甚至可以让你从 collection.

中删除项目
public static void ProceesData<T>(IList<T> param1, string date1, Action<T> func)
{            
    for (int i = param1.Count; i <= 0; i--)
    {
        func(param1[i]);
    }
}

这个调用会给你同样的结果:

ProceesData<A>(a_list, "", (x)=> { if (!x.isProcessed) x.isProcessed = true; });

通过这种方法你会变得更加灵活,因为你可以在每次调用时决定这个方法应该做什么。您甚至可以从 collection:

中删除已处理的项目
ProceesData<A>(a_list, "", (x)=> { if (!x.isProcessed) a_list.Remove(x); });

不过还是有一点不同。因为如果你有一个包含元素 AB 的 collection,就像这样:(我在这种情况下使用了一个示例性构造函数)

List<object> obj_list = new List<object>()
{
    new A(1, false),
    new B(2, true),
    new A(3, false),
    new B(4, false),
};

您必须为 Action 重载使用 dynamic 数据类型:

ProceesData<dynamic>(obj_list, "", (x) => { if (!x.isProcessed) obj_list.Remove(x); });

并且对于做事的反射方式,您需要在每次迭代时检查类型。这会增加处理时间。

public static void ProceesData<T>(IList<T> param1, string date1, string propertyName, object filterValue, object newValue)
{
    for (int i = param1.Count-1; i >= 0; i--)
    {
        PropertyInfo pi = param1[i].GetType().GetProperty(propertyName);
        object value;

        value = pi.GetValue(param1[i]);
        if (value.Equals(filterValue))
        {
            pi.SetValue(param1[i], newValue);
        }
    }
}