Understanding Generics—Examining the List(T) Type

Microsoft .NET Framework, ASP.NET, Visual C# (CSharp, C Sharp, C-Sharp) Developer Training, Visual Studio


Jump to: navigation, search
CSharp-Online.NET:Articles
C# Articles

Understanding Generics

© 2006 Andrew Troelsen

Examining the List<T> Type

Like nongeneric classes, generic classes are heap-allocated objects, and therefore must be new-ed with any required constructor arguments. In addition, you are required to specify the type(s) to be substituted for the type parameter(s) defined by the generic type. For example, System.Collections.Generic.List<T> requires you to specify a single value that describes the type of item the List<T> will operate upon. Therefore, if you wish to create three List<> objects to contain integers and SportsCar and Person objects, you would write the following:

static void Main(string[] args)
{
   // Create a List containing integers.
   List<int> myInts = new List<int>();
 
   // Create a List containing SportsCar objects.
   List<SportsCar> myCars = new List<SportsCar>();
 
   // Create a List containing Person objects.
   List<Person> myPeople = new List<Person>();
}

At this point, you might wonder what exactly becomes of the specified placeholder value. If you were to make use of the Visual Studio 2005 Code Definition View window, you will find that the placeholder T is used throughout the definition of the List<T> type. Here is a partial listing:

// A partial listing of the List<T> type.
namespace System.Collections.Generic
{
   public class List<T> :
      IList<T>, ICollection<T>, IEnumerable<T>,
      IList, ICollection, IEnumerable
   {
...
      public void Add(T item);
      public IList<T> AsReadOnly();
      public int BinarySearch(T item);
      public bool Contains(T item);
      public void CopyTo(T[] array);
      public int FindIndex(System.Predicate<T> match);
      public T FindLast(System.Predicate<T> match);
      public bool Remove(T item);
      public int RemoveAll(System.Predicate<T> match);
      public T[] ToArray();
      public bool TrueForAll(System.Predicate<T> match);
      public T this[int index] { get; set; }
..
   }
}

When you create a List<T> specifying SportsCar types, it is as if the List<T> type was really defined as so:

namespace System.Collections.Generic
{
   public class List<SportsCar> :
   IList<SportsCar>, ICollection<SportsCar>, IEnumerable<SportsCar>,
   IList, ICollection, IEnumerable
   {
...
   public void Add(SportsCar item);
   public IList<SportsCar> AsReadOnly();
   public int BinarySearch(SportsCar item);
   public bool Contains(SportsCar item);
   public void CopyTo(SportsCar[] array);
   public int FindIndex(System.Predicate<SportsCar> match);
   public SportsCar FindLast(System.Predicate<SportsCar> match);
   public bool Remove(SportsCar item);
   public int RemoveAll(System.Predicate<SportsCar> match);
   public SportsCar [] ToArray();
   public bool TrueForAll(System.Predicate<SportsCar> match);
   public SportsCar this[int index] { get; set; }
..
   }
}

Of course, when you create a generic List<T>, the compiler does not literally create a brand-new implementation of the List<T> type. Rather, it will address only the members of the generic type you actually invoke. To solidify this point, assume you exercise a List<T> of SportsCar objects as so:

static void Main(string[] args)
{
   // Exercise a List containing SportsCars
   List<SportsCar> myCars = new List<SportsCar>();
   myCars.Add(new SportsCar());
   Console.WriteLine("Your List contains {0} item(s).", myCars.Count);
}

If you examine the generated CIL code using ildasm.exe, you will find the following substitutions:

.method private hidebysig static void Main(string[] args) cil managed
{
   .entrypoint
   .maxstack 2
   .locals init ([0] class [mscorlib]System.Collections.Generic.'List`1'
      <class SportsCar> myCars)
   newobj instance void class [mscorlib]System.Collections.Generic.'List`1'
      <class SportsCar>::.ctor()
   stloc.0
   ldloc.0
   newobj instance void CollectionGenerics.SportsCar::.ctor()
   callvirt instance void class [mscorlib]System.Collections.Generic.'List`1'
      <class SportsCar>::Add(!0)
   nop
   ldstr "Your List contains {0} item(s)."
   ldloc.0
   callvirt instance int32 class [mscorlib]System.Collections.Generic.'List`1'
      <class SportsCar>::get_Count()
   box [mscorlib]System.Int32
   call void [mscorlib]System.Console::WriteLine(string, object)
   nop
   ret
}

Now that you’ve looked at the process of working with generic types provided by the base class libraries, in the remainder of this chapter you’ll examine how to create your own generic methods, types, and collections.


Previous_Page_.gif Next_Page_.gif

Personal tools