C#: Design Guide: Collection vs. Array

Design Guide: Collection vs. Array
Collections
"Collections": Types implementing IEnumerable, ICollection, or IList.
"Dictionaries": Types implementing IDictionary.
"collection" (lower case) refers to both Collections and Dictionaries.
– Do not postfix your collection names with List, Hashtable or any other word implying particular implementation.
– In rare cases, it’s OK to postfix some specialized collections w/ Queue, Stack, or Set (rather indicate specific semantics).
– Do implement strongly typed collections for value types; Any method storing or retrieving items from a collection should take or return objects of a specific type, not the generic Object type.
public ExceptionCollection: IList,  {
       object IList.this[int item] { … }
       public Exception this[int item] { … }
}
 
* IEnumerable and IEnumerator: MoveNext, Current, Reset
– The Current property returns a Object type—a cast is needed because you can’t implicitly downcast from Object to the item type.
Recommended: Use the is or the as operator to test that a type implements a given interface before attempting to use the type.
Quick Way: Implement collections storing reference types by delegating to a collection of reference types; For value types, do not do this.
 
* Protecting Data While Allowing Enumeration
class ItemStorage {
   class Enumerator : IEnumerator;
   {
      public Enumerable(IEnumerable enumerable) { enumerator = enumerable.GetEnumerator(); }
      IEnumerator enumerator;
      public IEnumerator GetEnumerator() { return enumerator; }
   }
   ArrayList items = new ArrayList();
   public IEnumerable ItemCollection
   {
      get
      {
         return new Enumerator(items);
      }
   }
}
ItemStorage items = new ItemStorage();
foreach (Object item in items.ItemCollection())
{
   Console.WriteLine(((Item)item).Name);
}
 
* Implement Strongly Typed Enumerators for Value Types
– First, change the way the item data is stored in the enumerator, e.g. moving from a reference-based collection to a value-based array.
– Second, have your own versions of GetEnumerator method and Current property (besides IEnumerable.GetEnumerator and IEnumerator.Current).
  Have strongly typed GetEnumerator to return a nested public struct called "Enumerator" and strongly typed Current to return the item type.
class ItemCollection {
   public struct Enumerator : IEnumerator;
   {
      public Enumerator(…) { … }
      public ValItem Current {
         get {
            return …;
         }
      }
      object IEnumerator.Current { get { return Current; } }
      ValItem items[4] = new ValItem[4];
   }
   public Enumerator GetEnumerator() { return new Enumerator(…); }
   IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
   ValItem items[4] = new ValItem[4];
}

ItemCollection items = new ItemCollection();
ItemCollection.Enumerator e = (ItemCollection.Enumerator)items.GetEnumerator();
while (e.MoveNext())
{
   Console.WriteLine(e.Current.Name);
}

 
* Use mutator interface allows to modify collected value types w/o requiring any boxing.
interface IValItem
{
   string Name { get; set; }
};
struct ValItem : IValItem
{
   public ValItem(string name) { this.name = name; }
   string name;
   public string name {
      get { return name; }
      set { name = value; }
   }
}
 
– Consider using keyed collections if the items stored in the collection have natural names; Keyed collections are collections that are indexed by a string-typed index and an integer-typed index.
– Do consider taking IEnumerable<T> as a parameter and dynamically check if the object implements ICollection<T> to implement a more efficient code.
public class List<T> : IList<T> {
   public List<T>(IEnumerable<T> collection){
      // check if it implements ICollection
      ICollection<T> col = collection as ICollection<T>;
 
      int numberOfNewItems=0;
      // optimized and slow code paths
      if(col!=null){
         numberOfNewItems = collection.Count;
      }
      else{
         IEnumerator<T> enumerator = collection.GetEnumerator();
         while(enumerator.MoveNext()) numberOfNewItems++;
      }
      this.Capacity = numberOfNewItems;
      foreach(T item in collection){
         Add(item);
      }
      this.TrimToSize();
   }
}
 
– Prefer implementing IEnumerable interface with optimistic concurrency for most usage scenarios and better performance.
Rationale:  There are two legitimate ways to implement the IEnumerable interface. The first is to assume that the collection will not be modified while it is being enumerated.  If it is modified throw an InvalidOperationException exception.  The 2nd way is to make a snap-shot of the collection in the enumerator thus isolating the enumerator from changes in the underlying collection.
 
Array vs. Collections.
– Do prefer collections over arrays in common scenarios. Collections provide more control over the contents of the container, are more version-able, and more usable.
TRADEOFF: For targeting lower level developers it is often better to use arrays for read-write scenarios. Arrays have a smaller memory footprint which helps reduce working set and access to elements in an array can be inlined by the JIT. However usability studies have shown that there are problems in the conceptual overhead of understanding the techniques to declare arrays in VB. Collections provide them a simple and consistent model. Array usage for read only scenarios is discouraged even in low level APIs as the cost of cloning the array is prohibitive.
– Do use a readonly collection or clone an array to prevent internal data from being changed by calling code.
– Recommended: Use collections to avoid the inefficiencies in cloning the array-valued properties:
for (int i = 0; i < obj.myObj.Count; i++) {
   DoSomething(obj.myObj[i]);
}
 
– Do not use a readonly fields of an array or any other mutable type.
– Consider using jagged arrays instead of multi-dimensional arrays; The CLR optimizes the access path of jagged arrays.
– General Rule: Do return an empty array or String.Empty() instead of a null reference since users assume this.

View in Web Browser

/_layouts/images/ichtmxls.gif

/sites/hmlee/eicar/_layouts/xlviewer.aspx?listguid={ListId}&itemid={ItemId}&DefaultItemOpen=1

0x0

0x1

FileType

xlsx

255

View in Web Browser

/_layouts/images/ichtmxls.gif

/sites/hmlee/eicar/_layouts/xlviewer.aspx?listguid={ListId}&itemid={ItemId}&DefaultItemOpen=1

0x0

0x1

FileType

xlsb

255

Snapshot in Excel

/_layouts/images/ewr134.gif

/sites/hmlee/eicar/_layouts/xlviewer.aspx?listguid={ListId}&itemid={ItemId}&Snapshot=1

0x0

0x1

FileType

xlsx

256

Snapshot in Excel

/_layouts/images/ewr134.gif

/sites/hmlee/eicar/_layouts/xlviewer.aspx?listguid={ListId}&itemid={ItemId}&Snapshot=1

0x0

0x1

FileType

xlsb

256

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s