Home Classroom C# .NET C# .NET Part 9

C# .NET Part 9


Welcome to another edition of the C#.NET article series in the Digit magazine. During the November edition of the article we concentrated on C# generics. In programming aspects, its applications are limited. So having a good understanding about the ‘Generics’ namespace is quite a handful.

Last time we learned how to declare Lists<> is C#. By using lists we can create an array of objects of any kind. This is a real nice feature in C# which would give the users a good control over what he is doing. In this article we would concentrate on some Generics in C#. Having a good understanding about these data types would expand your knowledge and would give more dimensions in coding efficient codes.

Generics in C#

Difference between C# generics and C++ templates

It’s no accident that the syntax of generics is similar to that of C++ templates, when the syntax for every other element in C# is based on the corresponding C++ syntax. This approach allows you to leverage your existing knowledge. As is typical throughout C#, the designers have streamlined the syntax and removed some of the verbosity. However, the similarities end there, because C# generics behave very differently than C++ templates, and, if you come from the C++ world, you must make sure that you understand the differences. Otherwise, you may find yourself attempting to apply your C++ template knowledge in ways that simply won’t work with generics. The main difference between the two is that expansion of generics is dynamic, whereas expansion of C++ templates is static. In other words, C++ templates are always expanded at compile time. Therefore, the C++ compiler must have access to all template types—generally through header files—and any types used to create the closed types from the template types at compile time. For this reason alone, it is impossible to package C++ templates into libraries. I know that many developers become confused by this fact when learning C++ templates for the first time. I remember plenty of times when it would have been nice to be able to package a C++ template into a static library or a DLL. Unfortunately, that is not possible. That’s why all of the code for C++ template types usually

lives in headers. This makes it difficult to package proprietary library code within C++ templates, since you must essentially give your code away to anyone who needs to consume it. The STL is a perfect example: Notice how almost every bit of your favorite STL implementation exists in header files. Generics, on the other hand, can be packaged in assemblies and consumed later. Instead of being formed at compile time, constructed types are formed at run time, or more specifically, at JITcompile time. In many ways, this makes generics more flexible. However, as with just about anything in the engineering world, advantages come with disadvantages.

Reference: Accelerated C# 2008 by Trey Nash (Chapter 11)

Support for generics is one of the nicest features of C# and .NET. Generics allow you to create open-ended types that are converted into closed types at run time. Each unique closed type is itself a unique type. Only closed types may be instantiated. When you declare a generic type, you specify a list of type parameters in the declaration for which type arguments are given to create closed,

Figure 1: Simple code for generics

In this case, a generic type is declared, MyCollection<T>, which treats the type within the collection as an unspecified type. In this example, the type parameter list consists of only one type, and it is described with syntax in which the generic types are listed, separated by commas, between angle brackets. The identifier T is really just a placeholder for any type. At some point, a consumer of MyCollection<T> will declare what’s called a closed type, by specifying the concrete type that T is supposed to represent. For example, suppose some other assembly wants to create a MyCollection<T> constructed type that contains members of type int.

Efficiency of C# Generics

  • Arguably, the added efficiency when using value types in collections is one of the greatest gains from generics in C#. Whereas a regular array based on System.Array can contain a heterogeneous collection of instances created from many types as long as it holds references to a common base type such as System.Object, it does come with its drawbacks.
  • Added type safety at compile time is always a good thing, because it’s much better to capture bugs based on type mismatches earlier at compile time rather than later at run time.

Generics are very important and useful if you work, for example, with any kind of collections. Because of the backwards compatibility of ASP.NET 2.0, the existing collections couldn’t be modified. Instead, a new namespace named System.Collections.Generic was created. It contains a lot of generic classes, structures, and interfaces like the following:

  • Dictionary
  • List
  • Queue
  • SortedDictionary
  • Stack

In the case of C#, generics are declared and type checked at compile time while instantiated at runtime just like any other object. C#Generics has the following advantages:

  1. The Program becomes statically typed, so errors are discovered at compile-time.
  2. No runtime casts are required and the program runs faster.
  3. Primitive type values (e.g int) need not be wrapped. Hence the program is faster and uses less space.

Reference: ASP.NET 2.0 Revealed, by Patrick Lorenz.

Generic Classes and Structs

Overall, declarations of all generic struct and class types follow the same rules as those for regular struct and class types. Any time a class declaration contains a type parameter list, it is, from that point on, a generic type. Likewise, any nested class declaration—whether it’s generic or not that is declared within the scope of a generic type is a generic type itself. That’s because the enclosing type’s fully qualified name requires a type argument in order to completely specify the nested type.

Generic types are overloaded based upon the number of arguments in their type argument

lists. The following example illustrates what I mean:

public class Container {}

public class Container<T> {}

public class Container<T, R> {}

Each of these declarations is valid within the same namespace. You can declare as many

generic types based on the Container identifier as you want, as long as each one has a different count of type parameters. You cannot declare another type named Container<X, Y>, even though the identifiers used in the type parameters list are different. The name overloading rules for generic declarations are based on the count of type parameters rather than the names given to their placeholders.

Generic Interfaces

Along with classes and structs, you can also create generic interface declarations. This concept is a natural progression from struct and class generics. Naturally, a whole host of interfaces declared within the .NET 1.1 base class library make excellent candidates to have generic versions fashioned after them. A perfect example is IEnumerable<T>. Generic containers create much more efficient code than nongeneric containers when they contain value types, since they avoid any unnecessary boxing. It’s only natural that any generic enumerable interface must have a means of enumerating the generic items within. Thus, IEnumerable<T> exists, and any enumerable containers you implement yourself should implement this interface. Alternatively, you could get it for free by deriving your custom containers from Collection<T>.

Generic Methods

C# supports generic methods. Any method declaration that exists within a struct, a class, or an interface may be declared as a generic method. That includes static as well as virtual or abstract methods. Also, you can declare generic methods on non-generic types. To declare a generic method, simply append a type argument list to the end of the method name but before the parameter list for the method. You can declare any of the types in the method parameter list, including the method return type, using one of the generic parameters. As with nested classes, it is bad form to hide outer type identifiers by reusing the same identifier in the nested scope, which in this case, is the scope of the generic method. Let’s consider an example of where a generic method may be useful.

In this article we gave a brief description about the Generics in C#.NET.  These can be used when you are in need to handle collections of data. From the next article we would dive into the world of Threads in C#.NET. Till then have a nice time..




Lakpriya Kottahachchi is an Undergraduate at the Faculty of Information Technology, University of Moratuwa. He enjoys programming with Java and C#.


Leave a Reply