A Few Things About Array Interfaces

Posted on January 21, 2007

One of the fun things about working with a big framework like .NET is that even after a few years of immersion in the technology, there are still fresh lessons to learn. So today, in the “I did NOT know that” department, I present a few interesting things about the humble array. The code snippets shown are part of a complete sample program that you can download here.

Arrays are enumerable

I did know this. Array implements IEnumerable, so you can iterate over the items in the array. There are two ways in C#. The direct way is to obtain an IEnumerator via IEnumerable.GetEnumerator(), and walk through the array:

IEnumerable enumerable = new Customer[] { bobSmith, johnDoe, johnGalt };
IEnumerator enumerator = enumerable.GetEnumerator();
while (enumerator.MoveNext())
{
    Customer c = (Customer)enumerator.Current;
    Console.WriteLine(c.ContactName);
}

Of course, most of us would prefer the simpler foreach construct, which does the same thing with more syntactic finesse:

IEnumerable enumerable = new Customer[] { bobSmith, johnDoe, johnGalt };
foreach (Customer c in enumerable)
{
    Console.WriteLine(c.ContactName);
}

An Array is an ICollection

In fact, Array implements a number of collection interfaces. Almost all are explicit interface implementations, which means that we have to cast to the collection type before we can use the methods and properties of the collection interface.

After IEnumerable, ICollection is the most primitive interface. If we look at an array as an ICollection, we can find the number of array elements with the Count property (equivalent to Array.Length). We can also see that an array is not synchronized (thread-safe), but it does offer a SyncRoot object for thread synchronization. The SyncRoot object happens to be the array itself.

Customer[] customers = new Customer[] { bobSmith, johnDoe, johnGalt };
ICollection collection = customers;
Console.WriteLine("Count = " + collection.Count.ToString());
Console.WriteLine("IsSynchronized: " +
   collection.IsSynchronized.ToString());
Console.WriteLine("SyncRoot: " +
   collection.SyncRoot == null ? "(null)" : collection.SyncRoot.ToString());
Console.WriteLine("SyncRoot == customers? " +
   (object.ReferenceEquals(collection.SyncRoot, customers) ?
     "Yes" : "No"));

An Array is an IList

Now things get interesting. The IsFixedSize property of IList returns true for an array, which makes sense. You have to declare an array with a specific size. However, Array does offers the Resize method, so is an array really fixed in size? The answer is yes – the Resize method really creates a new array of the specified size and changes the reference from the original array to the new array (that’s why the array parameter of Resize is a ref parameter).

The fixed size of Array has some other consequences. As an IList implementation, Array must support Add(), Clear(), Insert(), Remove(), and RemoveAt(). Because the size of the array cannot be changed, it doesn’t really make sense to add or delete items. All of these methods except Clear() throw NotSupportedException for this reason. Clear() does work, although it does not affect the size of the array – it simply sets the value of each array item to null (or the zero-bit equivalent for a value type array item).

Generic Interfaces

With the advent of .NET 2.0, generics were added to the framework, including generic versions of the collection interfaces. Thus we have IEnumerable, ICollection, and IList alongside IEnumerable, ICollection and IList. The generic interfaces allow us to refer to the array items in method parameters by their real types, instead of “object”. For example, instead of IList.Contains(object value), we can use IList.Contains(T value), where T is the array element type.

This is perhaps intuitive, as an array instance is strongly typed when it is declared. Array does in fact implement all of these generic interfaces. An array of Customer objects is an IEnumerable, ICollection, and IList.

As with the add/delete methods of IList, the corresponding methods of ICollection and IList throw NotSupportedException. Interestingly, ICollection.Clear() also throws NotSupportedException, which is inconsistent with the behavior of ICollection.Clear().

The support of generic interfaces in Array is very convenient. it allows for easy initialization of generic collections with arrays:

Customer[] customers = new Customer[] { bobSmith, johnDoe, johnGalt };
 
// Constructors requiring IList can use array of T.
Collection collection = new Collection(customers);
BindingList bindingList =
              new BindingList(customers);
 
// Constructor requiring IEnumerable can use array of T.
List list = new List(customers);

And of course, methods that return an IList, ICollection, or IEnumerable can simply return an array of T.

Who knew arrays were so cool?


No Replies to "A Few Things About Array Interfaces"


    Got something to say?

    Some html is OK