Collection Types - System.Collections.ObjectModel

For those of you who need to define your own collection types, you’ll find the types defined in the System.Collection.ObjectModel namespace most useful. In fact, you should derive your implementations from the objects in this namespace, if at all possible. This namespace contains only five types, and the fact that this namespace exists has been the source of some controversy. There were two main reasons these types were broken out into their own namespace. First, the Visual Basic environment already contains a Collection type that is implemented by a namespace it imports by default, and the Visual Basic team was concerned that VB users could become confused by seeing two types with similar names and drastically different behaviors popping up in IntelliSense. Second, the Base Class Libraries (BCL) team thought that users would rarely need the types in this namespace. Whether that is true will be shown over time. My opinion is that these types are extremely useful for writing libraries or for code consumed by others. One of Microsoft’s guidelines even suggests that you should consider creating a subclass of these types when exposing collections, even if only to provide a richer type name describing the collection and an easily accessible extensibility point.

These types are extremely useful if you’re defining collection types of your own. You can derive your type from Collection<T> easily in order to get default collection behavior, including implementation of ICollection<T>, IList<T>, and IEnumerable<T>. Collection<T> also implements the nongeneric interfaces ICollection, IList, and IEnumerable. However, you may have to cast the type to one of these interfaces explicitly to access the properties and methods of them, because many of them are implemented explicitly. Moreover, the Collection<T> type uses the NVI pattern7 to provide the derived type with a set of protected virtual methods that you can override. I won’t list the entire public interface to Collection<T> here, because you can find the details in the MSDN documentation. However, the protected virtual methods that you may override are shown in the following code:

public class Collection<T> : ICollection<T>, IList<T>, IEnumerable<T>,
ICollection, IList, IEnumerable
{
...
protected virtual void ClearItems();
protected virtual void InsertItem( int index, T item );
protected virtual void RemoveItem( int index );
protected virtual void SetItem( int index, T item );
...
}

You cannot modify the storage location of the collection by overriding these methods. Collection<T> manages the storage of the items, and the items are held internally through a private field of type IList<T>. However, you can override these methods to manage extra information triggered by these operations. Just be sure to call through to the base class versions in your overrides.

Finally, the Collection<T> type offers two constructors: one creates an empty instance, and the other accepts an IList<T>. The constructor copies the passed-in contents of the IList<T> instance into the new collection in the order that they are provided by the enumerator returned from IList<T>.GetEnumerator. This ordering is important to note, as you’ll see a way to control it in the following section on enumerators and iterator blocks. The implementation of the source list’s enumerator can do such things as reverse the order of the items as they’re put into the collection, simply by providing a proper enumerator implementation. Personally, I believe there should be more constructors on Collection<T> that accept an interface of type IEnumerator<T> and IEnumerable<T> in order to provide more flexible ways to fill a collection. You can solve this problem by introducing the extra constructors into a type that derives from Collection<T>, as I’ve shown here:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
public class MyCollection<T> : Collection<T>
{
public MyCollection() : base() {
}

public MyCollection( IList<T> list )
: base(list) { }

public MyCollection( IEnumerable<T> enumerable )
: base() {
foreach( T item in enumerable ) {
this.Add( item );
}
}

public MyCollection( IEnumerator<T> enumerator )
: base() {
while( enumerator.MoveNext() ) {
this.Add( enumerator.Current );
}
}
}

public class EntryPoint
{
static void Main() {
MyCollection<int> coll =
new MyCollection<int>( GenerateNumbers() );

foreach( int n in coll ) {
Console.WriteLine( n );
}
}

static IEnumerable<int> GenerateNumbers() {
for( int i = 4; i >= 0; —i ) {
yield return i;
}
}
}

In Main, you can see the instance of MyCollection<int> created by passing in an IEnumerable<int> type returned from the GenerateNumbers method. If the yield keyword in the GenerateNumbers method looks foreign to you, it may be because it’s a feature added in C# 2.0. I’ll explain this keyword a little later on in this chapter. Essentially, it defines what’s called an iterator block, which creates a compilergenerated enumerator from the code. After creating a MyCollection<T> constructed type, you can still hold on to it and use it solely through a Collection<T> reference. After all, MyCollection<T> is-a Collection<T>. Incidentally, I didn’t bother creating constructors that accept the nongeneric IEnumerable and IEnumerator, simply because I want to favor stronger type safety.

You may have noticed the existence of List<T> in the System.Collections.Generic namespace. It would be tempting to use List<T> in your applications whenever you need to provide a generic list type to consumers. However, instead of using List<T>, consider Collection<T>. List<T> doesn’t implement the protected virtual methods that Collection<T> implements. Therefore, if you derive your list type from List<T>, your derived type has no way to respond when modifications are made to the list. On the other hand, List<T> serves as a great tool to use when you need to embed a raw list-like storage implementation within a type, because it is devoid of virtual method calls such as Collection<T> and is more efficient as a result.

Another useful type within the System.Collections.ObjectModel namespace is the type ReadOnlyCollection<T>, which is a wrapper you can use to implement read-only collections. Since the C# language lacks any notion of using the const keyword for const-correctness like in C++, it is essential to create immutable types when necessary and pass those to methods in lieu of const parameters. The constructor for ReadOnlyCollection<T> accepts an IList<T> parameter type. Thus, you can use a ReadOnlyCollection<T> to wrap any type that implements IList<T>, including Collection<T>. Naturally, if users access the ICollection<T>.IsReadOnly property, the answer will be true. Any time users call a modifying method such as ICollection<T>.Clear, an exception of type NotSupportedException will be thrown. Moreover, in order to call modifying methods, the ReadOnlyCollection<T> reference must be cast to the interface containing the method, because ReadOnlyCollection<T> implements all modifying methods explicitly. The biggest benefit of implementing these methods explicitly is to help you avoid their use at compile time.

Source Of Information : Apress Accelerated C Sharp 2010

0 comments


Subscribe to Developer Techno ?
Enter your email address:

Delivered by FeedBurner