【问题标题】:How can I return an IOrderedEnumerable from a Collection based on OrderBy "property"如何从基于 OrderBy“属性”的集合中返回 IOrderedEnumerable
【发布时间】:2013-10-24 20:45:28
【问题描述】:
class Employee
{
    public string Name {get;set;}
    public int employee_id {get;set}
    public int Age {get;set}
}

Class EmployeeCollection : IEnumerable, IEnumerable<Employee>, IOrderedEnumerable<Employee>
{
    public Expression<Func<Employee, dynamic>> SortOrder {get;set;}
    protected Dictionary<int,Employee> EmployeeById = new Dictionary<int,Employee>();
    public void AddEmployee(Employee ePassed)
    {
        EmployeeById[ePassed.employee_id] = ePassed;
    }
    public IEnumerator<Employee> GetEnumerator()
    {
      foreach (int id in EmployeeByID.Keys)
      {
        yield return EmployeeById[id];
      }
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
      return this.GetEnumerator();
    }
    public IOrderedEnumerable<Employee> CreateOrderedEnumerable<TKey>(Func<Employee, TKey> KeySelector, IComparer<TKey> comparer, bool descending)
    {
      if (descending)
          return this.OrderByDescending(KeySelector, comparer);
      else
          return this.OrderBy(KeySelector, comparer);
    }
    public IEnumerable<Employee> OrderedObjects
    {
        if (this.SortOrder == null)
            return (IEnumerable<Employee>)this;  // No Sort Order applied
        else
        {
          // How do I get the "parameters" from SortOrder to pass into CreateOrderedEnumerable?
          throw new NotImplementedException();
        }
    }
}

我希望能够使用类似于以下的语法...

EmployeeCollection.SortOrder = (x => x.Name);
foreach (Employee e in EmployeeCollection.OrderedObjects)
{
  // Do something in the selected order
}

有数千个示例说明如何将排序、过滤等结果放入新的列表、集合、ObservableCollection 等,但如果您现有的集合已经响应事件,则会自动添加新对象以响应通知、用户操作,来自服务器的新数据等,然后所有这些功能要么“丢失”,要么必须“添加”,以使新的 List、Collection、ObservableCollection 等监听原始集合,以便以某种方式与原始集合已经知道和处理的所有各种更新、属性等......我希望能够让原始的“EmployeeCollection”简单地在请求的 SortOrder 中分发“Employee”对象......

我对“SortOrder”属性的语法做了一个“疯狂的猜测”,因为我想让 SortOrder 属性的语法类似于团队中其他开发人员习惯使用的 lambda 表达式的 orderby 部分通过查看 System.Linq.Enumerable 中类似于以下内容的扩展方法: public static IOrderedEnumerable&lt;TSource&gt; OrderBy&lt;ToSource, TKey&gt;(this IEnumerable&lt;TSource&gt; source, Func&lt;TSource, TKey&gt; keySelector);

我是 Linq、lambda 等的新手,如果我不知何故错过了 Linq/表达式树、谓词、匿名代表等其他人认为显而易见的关键方面,请提前道歉。

【问题讨论】:

  • 这正是EmployeeCollection.OrderBy(...) 会做的事情,并且具有更方便的引导语法。这样做有什么问题?
  • 这样做的问题是 OrderBy() 扩展方法返回 IEnumerable,它完全不知道原始 EmployeeCollection 的属性,没有连接到 EmployeeCollection 实现的 INotifyCollectionChanged 接口,等等等等……(你基本上得到了一个“愚蠢的对象列表”,它不知道如何做原始集合可以做的任何事情……
  • 所以?原来的收藏还是知道的。如果它发生了什么事,那么你知道你必须再次重新计算排序视图。这是更多的工作,但显然不可能免费保持任何数量的排序视图与“原始”同步。您只能在急切同步它们(如果您事先知道所有这些,这从 q 中根本不清楚)或懒惰之间进行选择,如上所述。

标签: c# linq lambda sql-order-by iorderedenumerable


【解决方案1】:

当您不使用ThenBy-type 操作(这些都是IOrderedEnumerable&lt;T&gt; 接口真正添加的)时,此方法有效。请参阅 C#: How to implement IOrderedEnumerable<T> 以获得支持该解决方案的解决方案。

public class Employee
{
    public string Name {get;set;}
    public int employee_id {get;set;}
    public int Age {get;set;}
}

public class EmployeeCollection : IEnumerable, IEnumerable<Employee>, IOrderedEnumerable<Employee>
{
    public Func<Employee, object> SortOrder {get;set;}
    public Func<Employee, bool> Filter {get;set;}
    protected Dictionary<int,Employee> EmployeeById = new Dictionary<int,Employee>();
    public void Add(Employee ePassed)
    {
        EmployeeById[ePassed.employee_id] = ePassed;
    }
    public IEnumerator<Employee> GetEnumerator()
    {
        var employees = EmployeeById.Keys.Select(id => this.GetEmployee(id));
        if (Filter != null)
            employees = employees.Where(Filter);
        if (SortOrder != null)
            employees = employees.OrderBy(SortOrder);
        return employees.GetEnumerator();
    }
    public Employee GetEmployee(int id)
    {
        return EmployeeById[id];
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
      return this.GetEnumerator();
    }
    public IOrderedEnumerable<Employee> CreateOrderedEnumerable<TKey>(Func<Employee, TKey> KeySelector, IComparer<TKey> comparer, bool descending)
    {
      throw new NotImplementedException();
    }
}

// this is some code you might use to test this:
var EmployeeCollection = new EmployeeCollection
{
    new Employee { employee_id = 1, Age = 20, Name = "Joe" },
    new Employee { employee_id = 2, Age = 30, Name = "Thomas" },
    new Employee { employee_id = 3, Age = 25, Name = "Robert" },
};
EmployeeCollection.SortOrder = x => x.Age;
EmployeeCollection.Filter = x => x.Name.Length > 4;
foreach (Employee e in EmployeeCollection)
{
    // do something
}

【讨论】:

  • @Wonderbird 我添加了对GetEmployeeFilter 的支持。
  • 感谢@tim-s,但实际的枚举器看起来更像以下内容。 (字典保存缓冲区而不是完全充实的对象。返回“Employee”的函数首先检查是否存在弱引用,如果它仍然有效,如果需要,在返回之前创建或重新创建实际的 Employee 实例它.. public IEnumerator GetEnumerator() { foreach (int id in EmployeeById.Keys) { Employee e = this.GetEmployee(id); if (this.Filter != null) { if (this.Filter(e) ) 产量回报 e; } 否则产量回报 e; } }
  • 您的示例(更新)肯定回答了我最初的问题/请求。但是我希望我有办法逐个对象应用过滤器对象。 (Employee 实际上是 EmployeeBase,其中 EmployeeSecret 和 EmployeePublic 类继承自它。) EmployeeCollectionBase 类似地具有继承自它的 EmployeeCollectionSecret 和 EmployeeCollectionPublic。如果 EmployeeCollectionSecret 没有定义其自己的 SortOrder/Filter,则尝试尊重 EmployeeCollectionBase 的 base.SortOrder / base.Filter 会将我送入“类型转换地狱”。
  • 是否有助于使其通用,EmployeeCollection&lt;T&gt; where T : EmployeeBase?然后Func 类型变成Func&lt;T, object&gt;,你不需要在你的子类上定义一个不同的类型,这一切都应该再次变得简单..
猜你喜欢
  • 1970-01-01
  • 2012-10-22
  • 2021-11-06
  • 2012-01-21
  • 1970-01-01
  • 2018-04-01
  • 1970-01-01
  • 2012-03-06
  • 1970-01-01
相关资源
最近更新 更多