Generics in Microsoft .NET
Part I: how to write generalized but still strongly typed code.

Article by Wim Uyttersprot (wim@u2u.be)
U2U - Brussels  (www.u2u.net)
Applies to
  • Visual Studio .NET Whidbey
  • C# 2.0.Alpha
  • VB.NET 2.0.Alpha

Summary At PDC 2003, Microsoft presented the next Microsoft OS with code name “Longhorn” and the next version of Visual Studio.NET code name “Whidbey”. For developers, the most important innovation is the fact that Microsoft .NET not only becomes entirely part of the OS, but even becomes the core API. Win32 API is definitely over. U2U (www.u2u.net) is preparing a series of articles on the innovations we may expect in the .NET Framework. One set of articles will describe the innovations in the .NET languages; topics will be how generics provide improved code reuse, how iterators simplify the implementation of enumerator patterns, how anonymous methods can ease your work with delegates, and how partial types provide easier development and code maintenance.

What are generics?

It looks like a paradox, but it isn’t: by means of .NET generics it becomes possible to write generalized code that is still strongly typed. The idea behind generics is to code with parameterized types instead of with concrete types. In this first article you will learn how to use generic types in order to create constructed types. The new generic collection types in the .NET framework will be discussed, and you will learn how to define your own generic collection types.

Generics are parameterized types (parameterized types are types that contain type parameters; a parameter type is a type that is used as parameter.) A generic type can be defined as a class, a struct, an interface or even as delegate but not as an enumeration. The difference between a normal type and a generic type is that in a generic type some fields, properties and arguments may be defined by means of a type parameter. For instance in a normal class all fields must be of well defined types; such as the types String, Integer, Color, Date… But in a generic class the type of a field might not be specified but instead be parameterized by means of a type parameter; a type parameter is a type that is not yet defined but that will be specified later: When we want to use a generic type, .NET wants us to specify a type parameter for each type parameter in order to make a constructed type.

n this article we will illustrate those notions through several code samples, e.g.:

In code snippet 5 we will illustrate:

·   the generic type
in C# syntax: ObjectList<ItemType>
in VB.NET syntax: ObjectList(Of ItemType)

·   the type parameter: ItemType

In code snippet 6 we will illustrate:

·   the type parameter: String

·   the constructed type
in C# syntax: ObjectList<string>
in VB.NET syntax: ObjectList(Of String)

Generics can also be methods that are using parameterize types. But this will be discussed in a later article.

Coding patterns

Most of our time, we are solving problems that we have solved already in a similar manner. Pattern writers typically lookout for such kind of recurring problems and define and document common solutions.

But if we want to be pattern developers, how do we write down our patterns in code?

The complicated strongly typed world

Class builders will typically use strong typing. Therefore they will specify Option Strict On in VB.NET or use only strongly typed languages as C#. But in the strongly typed world, writing general code is not that easy as the types of the references must match to the types of the objects. Typically inheritance and interfaces are used in order to solve the matching problems.

In this common approach, inheritance is used in the meaning of generalization: the general solution is coded by means of base classes and associations. The specialized solution is then coded in derived classes in which the virtual base methods and properties become overridden.

Generics and strong typing

But .NET generics offer us a new powerful technique that allows us to parameterize our types and that we can use in a lot of situations where strong typing is needed.

Generics are used for two main reasons: (1) for compile-time type safety as parameterized types can be checked (2) and for code reuse as generics allow us to implement patterns and algorithms that stay independent of types. Generics have the advantage that they allow us to write less code, that they increase the efficiency and therefore also the performance of our code.

.NET generics as they are defined in C# and VB.NET 1.2.Alpha can be compared to the C++ templates but are not identical. Generics simply do not have the goal to cover all the C++ generics scenarios. Therefore experienced C++ programmers must be very careful when designing C# or VB.NET generics as several C++ features are simply not supported in .NET; the reason is that in C++ templates are compile-time entities, and in .NET they are runtime entities.

Let us study .NET generics by example. In order to learn how to work with generics, we will use the new generic collection types List<T> and Dictionary<K,V> in .NET; Then, in order to learn the syntax needed for defining our own custom generic types, we will implement our own generic collection.

The (normal) collection types in .NET that are not type safe

The standard .NET collection types as for example the ArrayList and Hashtable are not type safe. They typically operate on the Object class as item type and as result there is simply no compiler control available when we want to collect our specialized objects.

As example we will create a collection object that is filled with three Color objects. With the non-generic Hashtable collection, we see that we have to make use of casting a lot. In code snippet 1, this casting is required on line 06. Let’s be frank: in order to accentuate the problem of casting, we use an explicit enumerator instead of using the foreach idiom (with foreach, no casting would be needed): the enumerator returned by GetEnumerator on line 10 is of the non-generic IDictionaryEnumerator type, and as result casting is needed on lines 13 and 14.

Working this way is not type safe as using wrong types will result in runtime exceptions.

C#

01:  Hashtable colors = new Hashtable();

02:  colors.Add("red", Color.Red);

03:  colors.Add("green", Color.Green);

04:  colors.Add("blue", Color.Blue);

05:

06:  Color color = (Color)colors["red"];   // <<< casting needed

07:  Console.WriteLine("{0} in RGB is {1}:{2}:{3}",

08:                    color, color.R, color.G, color.B);

09:

10:  IDictionaryEnumerator ie = colors.GetEnumerator();

11:  while (ie.MoveNext())

12:  {

13:    color = (Color)ie.Value;            // <<< casting needed

14:    string key = (string) ie.Key;       // <<< casting needed

15:    Console.WriteLine("{0} in RGB is {1}:{2}:{3}",

16:                      color, color.R, color.G, color.B);

17:  }

C# Code snippet 1: Use of the non-generic Hashtable collection: casting is required on lines 6, 13 and 14.

VB.NET

01:  Dim colors As Hashtable = New Hashtable

02:  colors.Add("red", Color.Red)

03:  colors.Add("green", Color.Green)

04:  colors.Add("blue", Color.Blue)

05:

06:  Dim c As Color = DirectCast(colors("red"), Color)   ' <<< casting needed

07:  Console.WriteLine("{0} in RGB is {1}:{2}:{3}", c, c.R, c.G, c.B)

08:

10:  Dim ie As IDictionaryEnumerator = colors.GetEnumerator()

11:  While ie.MoveNext()

13:      c = DirectCast(ie.Value, Color)                 ' <<< casting needed

14:      Dim key As String = DirectCast(ie.Key, String' <<< casting needed

15:      Console.WriteLine("{0} in RGB is {1}:{2}:{3}", c, c.R, c.G, c.B)

16:  End While

VB.NET Code snippet 1: Use of the non-generic Hashtable collection: casting is required on lines 6, 13 and 14.

Constructed types created from the generic collection types

The same code can be written by means of generic collections, with improved type safety as result. We will start with the simple List<T> and then discuss the Dictionary<K,V>. The generic collection types of the .NET Framework 1.2.Alpha are defined in the namespace System.Collections.Generic

A generic type is defined in a similar way as a non-generic type, but after the type name one or more type parameters are specified. Constraints can be added to the type parameter by means of the where keyword.

From a generic type we can create a constructed type. For instance we will define the colors collection as constructed type of the generic class List<T>. Figure 1 specifies that the <T> parameter must be derived from the Object type and therefore the intellisense uses here a new keyword, the “where” keyword.

Figure 1: The generic List<T> class as defined in the System.Collections.Generic namespace. Notice that the intellisense specifies that the type parameter <T> must be a base class of Object.

In order to construct our colors collection as a constructed type of a generic type, we must specify a type parameter for the type parameter: this will be the System.Drawing.Color structure, as it is the type of the collected item.

As result no casting is needed anymore in code snippet 2 as the indexer on line 06 is of type Color (compare this to code snippet 1 where casting was needed). Let’s emphasize that the reason why no casting is needed is the fact that the Color structure was specified on line 01 as the type argument of the generic List<T>. As a result, all members of the List<Color> class will declare the Color type instead of the <T> type parameter; for example in C# Code snippet 2:  on line 02 when using the “int List<T>.Add(T item)”-method, the signature of the method becomes “int List<Color>.Add(Color item)” as shown in Figure 2.

Figure 2: Intellisense for generic List type: notice the Color type instead of the type parameter <T>

Generic Enumerator types

The enumerators of generic collections are also generic: List<T>.Enumerator is the enumerator type of List<T>. By creating a constructed collection type, its nested enumerator is also constructed over the same type argument. For instance in code snippet 2 on line 10 the type returned by GetEnumerator is List<Color>.Enumerator, and therefore no casting is needed as the Current property of the enumerator on line 13 is also of type Color.

C#

using System.Collections.Generic;

...

 

01:  List<Color> colors = new List<Color>();

02:  colors.Add(Color.Red);

03:  colors.Add(Color.Green);

04:  colors.Add(Color.Blue);

05:

06:  Color color = colors[1];              // <<< no casting needed

07:  Console.WriteLine("{0} in RGB is {1}:{2}:{3}",

08:                    color, color.R, color.G, color.B);

09:  

10:  List<Color>.Enumerator ie = colors.GetEnumerator();

11:  while (ie.MoveNext())

12:  {

13:    color = ie.Current;                 // <<< no casting needed

14:    Console.WriteLine("{0} in RGB is {1}:{2}:{3}",

15:                      color, color.R, color.G, color.B);

16:  }

C# Code snippet 2: Use of the generic System.Collections.Generic.List<T> collection

VB.NET

Imports System.Collections.Generic

...

01:  Dim colors As List(Of Color) = New List(Of Color)

02:  colors.Add(Color.Red)

03:  colors.Add(Color.Green)

04:  colors.Add(Color.Blue)

05:

06:  Dim c As Color = colors(1)           ' <<< no casting needed

07:  Console.WriteLine("{0} in RGB is {1}:{2}:{3}", c, c.R, c.G, c.B)

08:

10:  Dim ie As List(Of Color).Enumerator = colors.GetEnumerator()

11:  While ie.MoveNext()

12:      c = ie.Current                   ' <<< no casting needed

14:      Console.WriteLine("{0} in RGB is {1}:{2}:{3}", c, c.R, c.G, c.B)

15:  End While

VB.NET Code snippet 2: Use of the generic System.Collections.Generic.List(Of T) collection

System.Collections.Generic.Dictionary<K,V> is the new dictionary or map collection offered by .NET that will be used in most places where the Hashtable was previously used, in order to improve the type safety of our code.

Figure 3:  The generic Dictionary class defined in the System.Collections.Generic namespace. Notice that the intellisense specifies that the parameterized types <K> and <V> must be both a base class of Object.

In order to create a colors collection, we specify the System.Drawing.Color structure again as type argument for the <V> type parameter as <V> represents the type of the collected item.

As argument for <K> we choose String so that it becomes the type for the index key. This results in a syntax where all members of the constructed Dictionary<string,Color> class specify the string and Color types, even in the code sense as figure 4 is showing.

Figure 4: Intellisense for the generic Dictionary type: notice the string type instead of type parameter <K> and the Color type instead of type parameter <V>.

The constructed dictionary type allows us to write type safe code. Look for example to the indexer (an indexer in C# corresponds to the default property in VB.NET) in code snippet 3 on line 06 that is of type Color.

Further the type returned by GetEnumerator is Dictionary<string,Color>.Enumerator, and therefore no casting is needed as the Current.Value property of the enumerator on line 13 returns a Color and as the Current.Key property on line 14 returns a string.

C#

using System.Collections.Generic;

...

01:  Dictionary<string,Color> colors = new Dictionary<string,Color>();

02:  colors.Add("red", Color.Red);

03:  colors.Add("green", Color.Green);

04:  colors.Add("blue", Color.Blue);

05:

06:  Color color = colors["red"];          // <<< no casting needed

07:  Console.WriteLine("{0} in RGB is {1}:{2}:{3}",

08:                    color, color.R, color.G, color.B);

09:

10:  Dictionary<string,Color>.Enumerator ie = colors.GetEnumerator();

11:  while (ie.MoveNext())

12:  {

13:    color = ie.Current.Value;           // <<< no casting needed

14:    string key = ie.Current.Key;        // <<< no casting needed

15:    Console.WriteLine("{0} in RGB is {1}:{2}:{3}",

16:                      color, color.R, color.G, color.B);

17:  }

C# Code snippet 3: Use of the generic System.Collections.Generic.Dictionary<K,V> collection

VB.NET

Imports System.Collections.Generic

...

01:  Dim colors As Dictionary(Of String,Color)=New Dictionary(Of String,Color)

02:  colors.Add("red", Color.Red)

03:  colors.Add("green", Color.Green)

04:  colors.Add("blue", Color.Blue)

05:

06:  Dim c As Color = colors("red")

07:  Console.WriteLine("{0} in RGB is {1}:{2}:{3}", c, c.R, c.G, c.B)

08:  

09:  Dim ie As Dictionary(Of String, Color).Enumerator =colors.GetEnumerator()

10:  While ie.MoveNext()

11:    c = ie.Current.Value

12:    Dim key As String = ie.Current.Key

13:    Console.WriteLine("{0} in RGB is {1}:{2}:{3}", c, c.R, c.G, c.B)

14:  End While

VB.NET Code snippet 3: Use of the generic System.Collections.Generic.Dictionary(Of K, V) collection

A simple example of a user-defined generic type

It is not that difficult to define our own generic type when we keep in mind that a type can become a parameter. Suppose we have coded two simple non-generic collections that both contain an ArrayList, and both define a type safe Add method and indexer. Hereby the Persons collection collects Person objects and the Orders collection groups Order objects. (We chose to make the example as simple as possible; it is a good exercise to start with a more complex collection.).

C#

public class Persons

{

  private ArrayList list;

 

  public Persons()

  {

    list = new ArrayList();

  }

 

  public Persons(int capacity)

  {

    list = new ArrayList(capacity);

  }

 

  public int Add(Person value)

  {

    return list.Add(value);

  }

 

  public Person this[int index]

  {

    get

    {

      return (Person)list[index];

    }

    set

    {

      list[index] = value;

    }

  }

}

public class Orders

{

  private ArrayList list;

 

  public Orders()

  {

    list = new ArrayList();

  }

 

  public Orders(int capacity)

  {

    list = new ArrayList(capacity);

  }

 

  public int Add(Order value)

  {

    return list.Add(value);

  }

 

  public Order this[int index]

  {

    get

    {

      return (Order)list[index];

    }

    set

    {

      list[index] = value;

    }

  }

}

C# Code snippet 4: Two non-generic collections with item types can be parameterized

VB.NET

Public Class Persons

  Dim list As ArrayList

 

  Public Sub New()

    list = New ArrayList

  End Sub

 

  Public Sub New(ByVal capacity _
                 
As Integer)

    list = New ArrayList(capacity)

  End Sub

 

  Public Function Add (ByVal value _

                  As Person) As Integer

    Return list.Add(value)

  End Function

 

  Default Public Property Item( _

      ByVal index As Integer) As Person

    Get

      Return DirectCast( _

            list.Item(index), Person)

    End Get

 

    Set(ByVal Value As Person)

      list.Item(index) = Value

    End Set

  End Property

End Class

Public Class Orders

  Dim list As ArrayList

 

  Public Sub New()

    list = New ArrayList

  End Sub

 

  Public Sub New(ByVal capacity _
                 
As Integer)

    list = New ArrayList(capacity)

  End Sub

 

  Public Function Add (ByVal value _

                  As Order) As Integer

    Return list.Add(value)

  End Function

 

  Default Public Property Item( _

      ByVal index As Integer) As Order

    Get

      Return DirectCast( _

            list.Item(index), Order)

    End Get

 

    Set(ByVal Value As Order)

      list.Item(index) = Value

    End Set

  End Property

End Class

VB.NET Code snippet 4: Two non-generic collections with item types can be parameterized

In the example of code snippet 4, it is simple to see a type that can be parameterized. The collection types Persons and Orders are quite identical as they only differ by the type of the items they collect: the Person class and the Order class.

Syntax for defining generic types

The simple syntax for defining a generic type consists of defining the type name and its type parameters. For a class this becomes:

C#

public class NameOfClass <TypeParameter>

{

    //...

}

VB.NET

Public Class NameOfClass (Of TypeParameter)

    ' ...

End Class

Our simple generic type ObjectList<ItemType>

Generics allow us to parameterize such types; we do this for example by defining the parameter <ItemType> that can represent the Person and Order classes but also any other type. As shown in code snippet 5 on line 01, we give our generic collection class the name ObjectList, the parameterized type name of our generic class becomes ObjectList<ItemType>.

C#

01:  public class ObjectList<ItemType>

02:  {

03:   private ArrayList list;

04:

05:    public ObjectList()

06:    {

07:      list = new ArrayList();

08:    }

09:

10:    public ObjectList(int capacity)

11:    {

12:      list = new ArrayList(capacity);

13:    }

14:

15:    public int Add(ItemType value)

16:    {

17:      return list.Add(value);

18:    }

19:

20:    public ItemType this[int index]

21:    {

22:      get

23:      {

24:        return (ItemType)list[index];

25:      }

26:      set

27:      {

28:        list[index] = value;

29:       }

30:    }

31:  }

 C# Code snippet 5: Our generic class ObjectList has one type parameter <ItemType> and uses for simplicity the ArrayList as embedded collection object. It defines a type safe Add method and indexer.

VB.NET

01:  Public Class ObjectList(Of ItemType As Object)

02:    Dim list ArrayList

03:

04:    Public Sub New()

05:      list = New ArrayList

06:    End Sub

07:  

08:    Public Sub New(ByVal capacity As Integer)

09:      list = New ArrayList(capacity)

10:    End Sub

11:  

12:    Public Function Add(ByVal value As ItemType) As Integer

13:      Return list.Add(value)

14:    End Function

15:  

16:    Default Public Property Item(ByVal index As Integer) As ItemType

17:      Get

18:        Return DirectCast(list.Item(index), ItemType)

19:      End Get

21:      Set(ByVal Value As ItemType)

22:        list.Item(index) = Value

23:      End Set

24:    End Property

25:  End Class

VB.NET Code snippet 5: Our generic class ObjectList has one type parameter “Of ItemType” and uses for simplicity the ArrayList as embedded collection object. It defines a type safe Add method and indexer.

The ClassView of Visual Studio.NET is able to list user-defined generic types as show in Figure 5.

Figure 5: An example of a user-defined generic type in ClassView.

Our constructed types

We can now create constructed types from our user-defined generic type by specifying an argument for the type parameter <ItemType>. On line 01 in code snippet 6, we use as argument the String class; on the next lines we use the constructed collection class as a collection of strongly typed String.

C#

01:  ObjectList<string> strings = new ObjectList<string>(10);

02:  strings.Add("Red");

03:  strings.Add("Green");

04:  strings.Add("Blue");

05:  string s = strings[1];

06:  Console.WriteLine("String {0} with length {1}", s, s.Length);

C# Code snippet 6: Coding with our user-defined generic types.

VB.NET

01:  Dim strings As ObjectList(Of String) = New ObjectList(Of String)(10)

02:  strings.Add("Red")

03:  strings.Add("Green")

04:  strings.Add("Blue")

05:  Dim s As String = strings(1)

06:  Console.WriteLine("String 0 with length 1", s, s.Length)

VB.NET Code snippet 6: Coding with our user-defined generic types.

Another technique to create new types from generic types is to take advantage of inheritance. For example in code snippet 7 the base class of Strings is the constructed class ObjectList<string>. The main advantage of this powerful technique is that we can use the derived classes in a familiar way as shown in code snippet 8.

C#

01:  public class Strings: ObjectList<stri&ng>

02:  {

03:  }

C# Code snippet 7: A powerful technique: using our constructed type as base class.

01:  Strings strings = new Strings();

02:  strings.Add("Red");

03:  strings.Add("Green");

04:  strings.Add("Blue");

05:  string s = strings[1];

06:  Console.WriteLine("String {0} with length {1}", s, s.Length);

C# Code snippet 8: Working with the Strings class of code snippet 7; its base class is a constructed type.

VB.NET

01:  Public Class Strings

02:      Inherits ObjectList(Of String)

03:  End Class

VB.NET Code snippet 7: A powerful technique: using our constructed type as base class.&

01:  Dim strings As Strings = New Strings

02:  strings.Add("Red")

03:  strings.Add("Green")

04:  strings.Add("Blue")

05:  Dim s As String = strings(1)

06:  Console.WriteLine("String 0 with length 1", s, s.Length)

VB.NET Code snippet 8: Working with the Strings class of of code snippet 7Error! Reference source not found.; its base class is a constructed type.

Generics are part of the IL language. When we use ILDASM to disassemble our assembly, we notice that ILDASM uses a special icon for generic types. For our example, Figure 6 shows for ObjectList the outline of the class symbol.

Figure 6: An example of a user-defined generic type in ILDASM.

The disassembled code of code snippet 6 is shown in snippet 9, where you can see that the type ObjectList<string> is explicitly revealed.

.method private hidebysig static void  TestStrings() cil managed

{

  // Code size       75 (0x4b)

  .maxstack  4

  .locals init (class U2U.ObjectList<string> V_0,

           string V_1)

  IL_0000:  ldc.i4.s   10

  IL_0002:  newobj     instance void class

            U2U.ObjectList<string>::.ctor(int32)

  IL_0007:  stloc.0

  IL_0008:  ldloc.0

  IL_0009:  ldstr      "Red"

  IL_000e:  callvirt   instance int32 class U2U.ObjectList<string>::Add(!0)

  IL_0013:  pop

  IL_0014:  ldloc.0

  IL_0015:  ldstr      "Green"

  IL_001a:  callvirt   instance int32 class U2U.ObjectList<string>::Add(!0)

  IL_001f:  pop

  IL_0020:  ldloc.0

  IL_0021:  ldstr      "Blue"

  IL_0026:  callvirt   instance int32 class U2U.ObjectList<string>::Add(!0)

  IL_002b:  pop

  IL_002c:  ldloc.0

  IL_002d:  ldc.i4.1

  IL_002e:  callvirt   instance !0 class

            U2U.ObjectList<string>::get_Item(int32)

  IL_0033:  stloc.1

  IL_0034:  ldstr      "String {0} with length {1}"

  IL_0039:  ldloc.1

  IL_003a:  ldloc.1

  IL_003b:  callvirt   instance int32 [mscorlib]System.String::get_Length()

  IL_0040:  box        [mscorlib]System.Int32

  IL_0045:  call       void [mscorlib]System.Console::WriteLine(string,

                                                                object,

                                                                object)

  IL_004a:  ret

} // end of method Client::TestStrings

Code snippet 9: The disassembled code of  C# Code snippet 6

9

Generic collections for value types

In the simple example of code snippet 5 where we defined our custom generic type ObjectList<ObjectType>, we used an embedded ArrayList collection. This works well if we only use reference types for the type parameter <ObjectType>, as shown with Person and Order.

But if we use a value type as type parameter, the embedded ArrayList is not optimal as boxing occurs each time when we add the value object to the ArrayList and as unboxing occurs when we retrieve the object from the ArrayList.

The new generic collection types in .NET have the advantage against their non-generic counterparts that they also work optimally with value types, i.e. without the need of using boxing/unboxing. Therefore it is better to use the generic type List<T> instead of the non-generic type ArrayList. So to end this article, let’s adapt our example of the ObjectList<ItemType> collection type and define the list reference to be of constructed type List<ItemType>. See line 3 of code snippet 10.

C#

01:  public class ObjectList<ItemType>

02:  {

03:   private List<ItemType> list;

04:

05:    public ObjectList()

06:    {

07:      list = new List<ItemType>();

08:    }

09:

10:    public ObjectList(int capacity)

11:    {

12:      list = new List<ItemType>(capacity);

13:    }

14:

15:    public int Add(ItemType value)

16:    {

17:      return list.Add(value);

18:    }

19:

20:    public ItemType this[int index]

21:    {

22:      get

23:      {

24:        return (ItemType)list[index];

25:      }

26:      set

27:      {

28:        list[index] = value;

29:       }

30:    }

31:  }

C# Code snippet 10: A better version of ObjectList, as on line 3 the embedded collection is defined of generic type List<T>

VB.NET

01:  Public Class ObjectList(Of ItemType As Object)

02:

03:    Dim list As List(Of ItemType)

04:

05:    Public Sub New()

06:      list = New List(Of ItemType)

07:    End Sub

08:  

09:    Public Sub New(ByVal capacity As Integer)

10:      list = New List(Of ItemType)(capacity)

11:    End Sub

12:  

13:    Public Function Add(ByVal value As ItemType) As Integer

14:      Return list.Add(value)

15:    End Function

16:  

17:    Default Public Property Item(ByVal index As Integer) As ItemType

18:      Get

19:        Return DirectCast(list.Item(index), ItemType)

21:      End Get

22:      Set(ByVal Value As ItemType)

23:        list.Item(index) = Value

24:      End Set

25:    End Property

26:  End Class

VB.NET Code snippet 10: A better version of ObjectList, as on line 3 the embedded collection is defined of generic type List<T>

Sample code

The sample code of the examples is available on the U2U website on http://www.u2u.net/downloads/generics1.zip

About the author

Wim Uyttersprot (wim@u2u.be) is founder and director of U2U. He is a Master in Science in Physics, is certified as MCSD and MCT in VB.NET, C# and C++.NET. U2U is as Microsoft Certified Technical Education Center specialized in .NET development. More info on www.u2u.net

 

Contact me Contact


Contact me Receive U2U Newsletter.
Looking for a challenging job Download Brochure On Site Training Looking for a challenging job
Favorites Favorites

Copyright © 1999-2010 by U2U