The SLAR on System.Collections.ArrayList

Continuing in the series on sharing some of the information in the .NET Framework Standard Library Annotated Reference Vol 1 here are some of the annotations on the System.ArrayList class.

 

 

JM The IndexOf(string, int, int) method used to have as its last parameter the name “lastIndex.” This name caused confusion regarding whether the index was included in the search or not (inclusive or exclusive). Thus, as part of the standardization process the name got changed to “count” to avoid that confusion.

BG Note the heavy use of wrapper collections on ArrayList to add in functionality like thread safety or making a list read-only. This is very useful in that it allows users to write their code using ArrayList and then add in thread safety without having to change more than one line of code. At least, that was the original goal. However, there are two drawbacks with this approach. One is a subtle performance penalty — every method call on ArrayList is virtual, meaning it cannot be inlined and is therefore slower than it could be. The second is that the design isn’t quite what people need, at least for synchronization. Sometimes users may want code that does two operations with an ArrayList in a consistent state, like this:

ArrayList list = ArrayList.Synchronized(someOtherArrayList);

if (list.Count > 0)

   Object node = list[0];

At this point, our threadsafe wrapper will not work as expected. True, the Count property will be accessed in a thread-safe way, as will the first element of the ArrayList. However, the way our design for the threadsafe wrapper works requires that we take a lock when calling each method, then release the lock after calling each method. Therefore, after we have retrieved the Count property, another thread could add or remove an element from the list, meaning that the next line might fail. The correct way to write code like this would be to use the SyncRoot property:

lock(list.SyncRoot) {

   if (list.Count > 0)

      Object node = list[0];

}

This code is correct and has the added benefit of taking the lock only once, instead of twice. Note the thread-safe wrapper collections are expected to use the SyncRoot property (defined on ICollection) to implement their thread-safety guarantees, so doing this in one part of your code is fully compatible with using a thread-safe wrapper in a different section of your code.

 

For these reasons, the SyncRoot property is a great idea and the idea of a thread-safe wrapper is less appealing than you’d like to believe. If we were designing collections over again, we might not allow subclasses of our collections for performance reasons and because they don’t add a lot of value. The IList interface should be rich enough for most users, and if it isn’t, the correct approach is to write another interface. Classes implementing that interface are free to use ArrayList internally for their own implementation details if necessary.

And here is a sample application:

using System;

using System.Collections;

namespace Samples

{

  public class ArrayListConstructor

  {

    public static void Main()

    {

      string[] s = {"damien", "mark", "brad"};

      ArrayList a = new ArrayList(s);

      Console.WriteLine("Count: {0}", a.Count);

      Console.WriteLine("Default Capacity: {0}", a.Capacity);

      PrintElements(a);

      s[0] = "maire";

      s[1] = "sacha";

      s[2] = "tamara";

      PrintElements(a);

    }

    public static void PrintElements(IEnumerable ie)

    {

      IEnumerator e = ie.GetEnumerator();

      while(e.MoveNext())

        Console.Write("{0} ", e.Current );

      Console.WriteLine();

    }

  }

}

 

The output is

Count: 3

Default Capacity: 3

damien mark brad

damien mark brad

 

---------------

Amazon.com Sales Rank: 7,992 (up 3676 from last post)