具有隐式排序顺序的 IList

发布于 2024-08-23 14:37:15 字数 1322 浏览 11 评论 0原文

我想创建一个 IList ,它始终以默认/隐式排序顺序维护其 Child 对象(即,无论对底层进行添加/删除)列表)。

我特别想要避免的是,所述 IList 的所有使用者每次想要时都需要显式调用 IEnumerable.OrderBy()列举一下。除了违反 DRY 之外,这种方法还会破坏封装,因为消费者必须知道我的列表甚至已排序,这实际上与他们无关:)

看起来最合乎逻辑/最有效的解决方案是公开 IList< Child> 作为 IEnumerable(以防止 List 突变)并向包含的 Parent 添加显式添加/删除方法。这样,我可以拦截需要重新排序的列表更改,并通过 Linq 应用更改:

public class Child {
  public string StringProperty;
  public int IntProperty;
}

public class Parent{
private IList<Child> _children = new List<Child>();

      public IEnumerable<Child> Children{
      get
         {

            return _children;
         }
      }
      private void ReSortChildren(){
        _children = new List<Child>(child.OrderBy(c=>c.StringProperty));
      }
      public void AddChild(Child c){
          _children.Add();
          ReSortChildren()
      }
      public void RemoveChild(Child c){
          _children.Remove(c);
          ReSortChildren()
      }
}

不过,这种方法不会拦截对底层 Child.StringProperty 所做的更改(在本例中case 是驱动排序的属性)。对于这样一个基本问题一定有一个更优雅的解决方案,但我还没有找到。

编辑: 我不清楚我是否更喜欢 LINQ 兼容的解决方案。我宁愿不诉诸使用 .NET 2.0 构造(即 SortedList)

I'd like to create an IList<Child> that maintains its Child objects in a default/implicit sort order at all times (i.e. regardless of additions/removals to the underlying list).

What I'm specifically trying to avoid is the need for all consumers of said IList<Child> to explicitly invoke IEnumerable<T>.OrderBy() every time they want to enumerate it. Apart from violating DRY, such an approach would also break encapsulation as consumers would have to know that my list is even sorted, which is really none of their business :)

The solution that seemed most logical/efficient was to expose IList<Child> as IEnumerable<Child> (to prevent List mutations) and add explicit Add/Remove methods to the containing Parent. This way, I can intercept changes to the List that necessitate a re-sort, and apply one via Linq:

public class Child {
  public string StringProperty;
  public int IntProperty;
}

public class Parent{
private IList<Child> _children = new List<Child>();

      public IEnumerable<Child> Children{
      get
         {

            return _children;
         }
      }
      private void ReSortChildren(){
        _children = new List<Child>(child.OrderBy(c=>c.StringProperty));
      }
      public void AddChild(Child c){
          _children.Add();
          ReSortChildren()
      }
      public void RemoveChild(Child c){
          _children.Remove(c);
          ReSortChildren()
      }
}

Still, this approach doesn't intercept changes made to the underlying Child.StringProperty (which in this case is the property driving the sort). There must be a more elegant solution to such a basic problem, but I haven't been able to find one.

EDIT:
I wasn't clear in that I would preferable a LINQ compatible solution. I'd rather not resort to using .NET 2.0 constructs (i.e. SortedList)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

相思碎 2024-08-30 14:37:15

使用 SortedList 怎么样?

What about using a SortedList<>?

强辩 2024-08-30 14:37:15

您可以采取的一种方法是让 Child 发布一个 OnStringPropertyChanged 事件,该事件传递 StringProperty 的先前值。然后创建 SortedList 的派生,它重写 Add 方法以将处理程序连接到该事件。每当事件触发时,从列表中删除该项目并使用 StringProperty 的新值重新添加它。如果您无法更改 Child,那么我将创建一个派生自或包装 Child 的代理类来实现该事件。

如果您不想这样做,我仍然会使用 SortedList,但在需要更改 StringProperty 时在内部管理上述排序逻辑。为了做到 DRY,最好通过正确管理排序的通用方法将所有更新路由到 StringProperty,而不是直接从类中的各个位置访问列表并重复排序管理逻辑。

我还要警告不要允许控制器传入对 Child 的引用,这允许他在将 StringProperty 添加到列表后对其进行操作。

public class Parent{
  private SortedList<string, Child> _children = new SortedList<string, Child>();

  public ReadOnlyCollection<Child> Children{
    get { return new ReadOnlyCollection<Child>(_children.Values); }
  }

  public void AddChild(string stringProperty, int data, Salamandar sal){
    _children.Add(stringProperty, new Child(stringProperty, data, sal));
  }

  public void RemoveChild(string stringProperty){
    _children.Remove(stringProperty);
  }

  private void UpdateChildStringProperty(Child c, string newStringProperty) {
    if (c == null) throw new ArgumentNullException("c");

    RemoveChild(c);
    c.StringProperty = newStringProperty;
    AddChild(c);
  }

  public void CheckSalamandar(string s) {
    if (_children.ContainsKey(s))
      var c = _children[s];
      if (c.Salamandar.IsActive) {
        // update StringProperty through our method
        UpdateChildStringProperty(c, c.StringProperty.Reverse());
        // update other properties directly
        c.Number++;
    }
  }
}

One way you could go about it is to have Child publish an event OnStringPropertyChanged which passes along the previous value of StringProperty. Then create a derivation of SortedList that overrides the Add method to hookup a handler to that event. Whenever the event fires, remove the item from the list and re-add it with the new value of StringProperty. If you can't change Child, then I would make a proxy class that either derives from or wraps Child to implement the event.

If you don't want to do that, I would still use a SortedList, but internally manage the above sorting logic anytime the StringProperty needs to be changed. To be DRY, it's preferable to route all updates to StringProperty through a common method that correctly manages the sorting, rather than accessing the list directly from various places within the class and duplicating the sort management logic.

I would also caution against allowing the controller to pass in a reference to Child, which allows him to manipulate StringProperty after it's added to the list.

public class Parent{
  private SortedList<string, Child> _children = new SortedList<string, Child>();

  public ReadOnlyCollection<Child> Children{
    get { return new ReadOnlyCollection<Child>(_children.Values); }
  }

  public void AddChild(string stringProperty, int data, Salamandar sal){
    _children.Add(stringProperty, new Child(stringProperty, data, sal));
  }

  public void RemoveChild(string stringProperty){
    _children.Remove(stringProperty);
  }

  private void UpdateChildStringProperty(Child c, string newStringProperty) {
    if (c == null) throw new ArgumentNullException("c");

    RemoveChild(c);
    c.StringProperty = newStringProperty;
    AddChild(c);
  }

  public void CheckSalamandar(string s) {
    if (_children.ContainsKey(s))
      var c = _children[s];
      if (c.Salamandar.IsActive) {
        // update StringProperty through our method
        UpdateChildStringProperty(c, c.StringProperty.Reverse());
        // update other properties directly
        c.Number++;
    }
  }
}
夏了南城 2024-08-30 14:37:15

我认为如果您从 KeyedCollection,你会得到你需要的。但这只是基于阅读文档。

编辑:

如果这有效,那么不幸的是,这并不容易。这个家伙中的底层查找字典和底层列表都没有排序,它们也没有足够暴露以便您能够替换它们。但是,它可能会提供一种模式供您在自己的实现中遵循。

I think that if you derive from KeyedCollection, you'll get what you need. That is only based on reading the documentation, though.

EDIT:

If this works, it won't be easy, unfortunately. Neither the underlying lookup dictionary nor the underlying List in this guy is sorted, nor are they exposed enough such that you'd be able to replace them. It might, however, provide a pattern for you to follow in your own implementation.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文