Pages

Thursday, May 8, 2014

C# - Generics

Concept of type parameters

// Declare the generic class. 
public class GenericList<T>
{
    void Add(T input) { }
}
class TestGenericList
{
    private class ExampleClass { }
    static void Main()
    {
        // Declare a list of type int.
        GenericList<int> list1 = new GenericList<int>();

        // Declare a list of type string.
        GenericList<string> list2 = new GenericList<string>();

        // Declare a list of type ExampleClass.
        GenericList<ExampleClass> list3 = new GenericList<ExampleClass>();
    }
}
  • You can create your own generic interfaces, classes, methods, events and delegates.
  • Generic classes may be constrained to enable access to methods on particular data types.
  • Information on the types that are used in a generic data type may be obtained at run-time by using reflection.
  • Generic classes and methods combine reusability, type safety and efficiency
  •  Generics are most frequently used with collections and the methods that operate on them.
  • Generics provide the solution to a limitation in earlier versions of the common language runtime and the C# language in which generalization is accomplished by casting types to and from the universal base type Object
  • By creating a generic class, you can create a collection that is type-safe at compile-time.
  •  ArrayList is a highly convenient collection class that can be used without modification to store any reference or value type.
// The .NET Framework 1.1 way to create a list:
System.Collections.ArrayList list1 = new System.Collections.ArrayList();
list1.Add(3);
list1.Add(105);
 
System.Collections.ArrayList list2 = new System.Collections.ArrayList();
list2.Add("It is raining in Redmond.");
list2.Add("It is snowing in the mountains."); 
 
  • But this convenience comes at a cost. Any reference or value type that is added to an ArrayList is implicitly upcast to Object. If the items are value types, they must be boxed when they are added to the list, and unboxed when they are retrieved. Both the casting and the boxing and unboxing operations decrease performance; the effect of boxing and unboxing can be very significant in scenarios where you must iterate over large collections.
  • The other limitation is lack of compile-time type checking; because an ArrayList casts everything to Object, there is no way at compile-time to prevent client code from doing something such as this:

System.Collections.ArrayList list = new System.Collections.ArrayList();
// Add an integer to the list.
list.Add(3);
// Add a string to the list. This will compile, but may cause an error later.
list.Add("It is raining in Redmond.");
 
int t = 0;
// This causes an InvalidCastException to be returned.
foreach (int x in list)
{
    t += x;
}

 Although perfectly acceptable and sometimes intentional if you are creating a heterogeneous collection, combining strings and ints in a single ArrayList is more likely to be a programming error, and this error will not be detected until runtime.
  • In versions 1.0 and 1.1 of the C# language, you could avoid the dangers of generalized code in the .NET Framework base class library collection classes only by writing your own type specific collections. Of course, because such a class is not reusable for more than one data type, you lose the benefits of generalization, and you have to rewrite the class for each type that will be stored. 

What ArrayList and other similar classes really need is a way for client code to specify, on a per-instance basis, the particular data type that they intend to use. That would eliminate the need for the upcast to T:System.Object and would also make it possible for the compiler to do type checking. In other words, ArrayList needs a type parameter. That is exactly what generics provide. In the generic List<T> collection, in the N:System.Collections.Generic namespace, the same operation of adding items to the collection resembles this:

// The .NET Framework 2.0 way to create a list
List<int> list1 = new List<int>();
 
// No boxing, no casting:
list1.Add(3);
 
// Compile-time error:
// list1.Add("It is raining in Redmond.");
 
For client code, the only added syntax with List<T> compared to ArrayList is the type argument in the declaration and instantiation. In return for this slightly more coding complexity, you can create a list that is not only safer than ArrayList, but also significantly faster, especially when the list items are value types.


class Program
    {
        static void Main(string[] args)
        {           
            var add1 = new Add<string>();
            add1.AddNumbers("var");
        } 

        public class Add<T>
        {        
            public void AddNumbers(T input)
            {
                Console.WriteLine(input);
                Console.ReadKey();
            }
        }
    }
 

 
class Program
    {
        static void Main(string[] args)
        {
            var add = new Add<int>(5);
            add.Show();
        }
 
        public class Add<T>
        {
            private T num;
 
            public Add(T t)
            {
                num = t;
            }
 
            public void Show()
            {
                Console.WriteLine(num);
                Console.ReadKey();
            }
        }
    }

 

 

No comments:

Post a Comment