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

时间:2017-02-27 17:45:13

标签: c# .net generics generic-collections

我有两个类具有以下属性

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

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

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

}

我需要关注的事情

  1. 在ProcessData方法中,我想过滤具有的项目 isProcessed标志是“True”。
  2. 此外,我想迭代此集合,需要为IsProcessed属性设置值。
  3. 注意:我更喜欢使用反射的解决方案,因为属性名称是常量(即“IsProcessed”)

    任何人都可以帮忙解决这个问题。

3 个答案:

答案 0 :(得分:6)

最简单的方法是确保两个类都实现一个通用接口并约束您的泛型方法。例如:

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

答案 1 :(得分:0)

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

答案 2 :(得分:0)

  

注意:我更喜欢使用反射解决方案

此方法将迭代集合,根据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循环,因为这甚至允许你从你的集合中删除项目。

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

通过这种方法你会变得更加灵活,因为你可以在每次调用时决定这个方法应该做什么。您甚至可以从集合中删除已处理的项目:

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

但仍有一个区别。因为如果你有一个包含元素AB的集合,就像这样:(我在这种情况下使用了一个示例构造函数)

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

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

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