【问题标题】:How to filter a collection inside a generic method如何在泛型方法中过滤集合
【发布时间】:2017-02-27 17:45:13
【问题描述】:

我有两个类具有以下属性

 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 标志为“真”。
  2. 另外我想迭代这个集合并且需要为 IsProcessed 属性设置值。

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

谁能帮忙解决这个问题。

【问题讨论】:

  • 你能让AB实现同一个接口吗?
  • 您的类AB 没有共同祖先或公开接口的任何原因?这样就很容易解决问题了。
  • 因为 isProcessed 对 A 类和 B 类是通用的,我不打算改变类结构
  • 我认为你应该有与 isProcessed() 方法的通用接口,并且两个类都应该实现它。因此,如果 . ,您可以使用您的界面
  • ...since property name is constant

标签: c# .net generics generic-collections


【解决方案1】:

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

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&lt;T&gt; 作为参数传递给您的方法。例如:

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

【讨论】:

【解决方案2】:

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

【讨论】:

  • 我更喜欢使用反射的解决方案,因为属性名称是常量“IsProcessed”
  • 我需要一个类似于 typeof(T).GetProperty("IsProcessed") 的解决方案
【解决方案3】:

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

此方法将迭代集合,根据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),
};

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

【讨论】:

    猜你喜欢
    • 2016-07-26
    • 1970-01-01
    • 2023-03-24
    • 2012-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多