Monday, July 14, 2008

A generic ListCollection class. Take 2.

I blogged recently about using public Collection instead of public List. The Visual Studio Code Analysis Team explained in their blog how to re-use List methods in classes derived from Collection<T> generic class. As you can see, their approach requires your class constructor to call base constructor of List<T>. To make developer's life easier, I proposed to make a generic ListCollection<T> class with such a constructor and inherit custom collection classes from it.
Then I read The Missing .NET #2: Collection<T> AddRange() article by Jason Kemp. He implemented "missing" methods of Collection<T> as extension methods. I added the same methods to my ListCollection<T> class. Because a default constructor of ListCollection<T> class guarantees that its protected Items property actually contains a List<T>, it is easier to implement "missing" methods; you just call corresponding methods of List<T>:


  /// <summary>
  /// A generic Collection class which ensures that it actually contains generic List.
  /// This provides us with an ability to re-use any public method of List&lt;T&gt; with a simple shell function.
  /// Such a shell could be implemented in a concrete implementation of this generic class,
  /// as extensions methods for ListCollection&lt;T&gt; or directly in ListColection&lt;T&gt; as it is done below.
  /// See "The Missing .NET #2: Collection&lt;T&gt; AddRange()"
  /// <see cref="http://www.ageektrapped.com/blog/the-missing-net-2-collectiont-addrange/"/> as well.
  /// </summary>
  public class ListCollection<T> : Collection<T>
    where T : class {
    public ListCollection()
      : base(new List<T>()) {
    }
 
    public void AddRange(IEnumerable<T> values) {
      ((List<T>)this.Items).AddRange(values);
    }
 
    public bool Exists(Predicate<T> match) {
      return ((List<T>)this.Items).Exists(match);
    }
 
    public T Find(Predicate<T> match) {
      return ((List<T>)Items).Find(match);
    }
 
    public ListCollection<T> FindAll(Predicate<T> match) {
      ListCollection<T> items = new ListCollection<T>();
      items.AddRange(((List<T>)Items).FindAll(match));
      //foreach (var item in ((List<T>)Items).FindAll(match)) {  //You can use this as an alternative to the above line
      //  items.Add(item);
      //}
      return items;
    }
 
    public int FindIndex(Predicate<T> match) {
      return ((List<T>)Items).FindIndex(match);
    }
 
    public int FindIndex(int startIndex, Predicate<T> match) {
      return ((List<T>)Items).FindIndex(startIndex, match);
    }
 
    public int FindIndex(int startIndex, int count, Predicate<T> match) {
      return ((List<T>)Items).FindIndex(startIndex, count, match);
    }
 
    public T FindLast(Predicate<T> match) {
      return ((List<T>)Items).FindLast(match);
    }
 
    public int FindLastIndex(Predicate<T> match) {
      return ((List<T>)Items).FindLastIndex(match);
    }
 
    public int FindLastIndex(int startIndex, Predicate<T> match) {
      return ((List<T>)Items).FindLastIndex(startIndex, match);
    }
 
    public int FindLastIndex(int startIndex, int count, Predicate<T> match) {
      return ((List<T>)Items).FindLastIndex(startIndex, count, match);
    }
 
    public void ForEach(Action<T> action) {
      ((List<T>)Items).ForEach(action);
    }
 
    public int RemoveAll(Predicate<T> match) {
      return ((List<T>)Items).RemoveAll(match);
    }
 
    public bool TrueForAll(Predicate<T> match) {
      return ((List<T>)Items).TrueForAll(match);
    }
 
  }


An update: I added a "conversion" constructor Collection<T>(IList) to my ListCollection<T> generic class. It is useful and even made one method of this class itself simpler:


 
    // A default constructor
    public ListCollection() : base(new List<T>()) { }
 
    // A "converter" constructor, which does not copy the list passed in.
    // See http://msdn.microsoft.com/en-us/library/ms132401.aspx
    public ListCollection(IList<T> list) : base(list) { }



 
    public ListCollection<T> FindAll(Predicate<T> match) {
      return new ListCollection<T>(((List<T>)Items).FindAll(match));
    }