8. Generics

About this Tutorial

Objectives

Delegates will learn to develop applications using C# 4.5. After completing this course, delegates will be able to:

  • Use Visual Studio 2012 effectively
  • Create commercial C# Web Applications
  • Develop multi-threaded applications, Use WCF and LINQ

Audience

This course has been designed primarily for programmers new to the .Net development platform. Delegates experience solely in Windows application development or earlier versions of C# will also find the content beneficial.

Prerequisites

No previous experience in C# programming is required. But any experience you do have in programming will help. Also no experience in Visual Studio is required. But again any experience you do have with programming development environments will be a valuable.

Download Solutions

HTML tutorial


Overview

  1. Since .NET 2.0, data types can be parameterised on the types of data they hold and manipulate – You can define generic classes, structs, interfaces, and delegates.
  2. Also, methods can be parameterised on the types of arguments they receive – You can define generic methods, to implement algorithms in a type-safe manner.
  3. The main benefit of generics:
    • Enable you to represent general data structures (e.g. collections) and general algorithms, more type-safe than object references.
    • Note: the .NET Framework class library defines a suite of generic collection classes (see System.Collections.Generic).

Estimated Time – 2 Hour

Not what you are looking? Try the next tutorial – Using .NET Framework APIs

Lab 1: Getting Started with Generics

Lab 1: Getting Started with Generics

Overview

  1. Consider the following Queue class – Implemented in C# without using generics
    public class RawQueue
    {
     private object[] elems;
     private int numElems, maxElems;
     public void AddItem(object item) {...}
     public object RemoveItem() {...}
    }
  2. View code file.
  3. Pros of the Queue class:
    • It can hold any kind of item, so it is reusable.
  4. Cons of the Queue class:
    • Allows heterogeneous collections (is this what you want?).
    • When an item is retrieved, it must be cast to the correct data type.
  5. Example of Generic Types:
    • Here is a revised Queue class, using generics – <T> defines T as a type parameter, a placeholder for “any type” –
      public class GenQueue<T>
      {
       private T[] elems;
       private int numElems, maxElems;
       public void AddItem(T item) {...}
       public T RemoveItem() {...}
      }
    • View code file.
    • You can now use Queue in a type-safe manner
      // Create a GenQueue instance that can only contain integers.
      GenQueue<int> myQueue = new GenQueue<int>(10);
      // This GenQueue instance only accepts integers for AddItem().
      myQueue.AddItem(42); // OK
      myQueue.AddItem("Oh no you don't"); // Type mismatch error.
      // This GenQueue instance returns an integer from RemoveItem().
      int x = myQueue.RemoveItem(); // OK
      string s = myQueue.RemoveItem(); // Type mismatch error.
    • View code file.
Lab
  1. Defining Multiple Type Parameters.
  2. Open Visual Studio, C#>Windows>Console Application and call this EmployeeDict
  3. Form

  4. We are going to create an employee dictionary that will hold a generic dictionary of staff that can be found using their unique key values.
  5. To start we will create a new class called employee to hold staff information for this at the moment it will just be a name –
    public class Employee
     {
      private string name;
      public Employee(string name)
      {
       this.name = name;
      }
      public override string ToString()
      {
       return name;
      }
     }
  6. View code file.
  7. Next we need to create the most complex part the generic dictionary. To make it easier to understand to start with we will just start with the class and structure for entry that will define how each entry should be ordered –
    public class GenDictionary<K, V>
     {
      // Nested class, to represent one key/value entry in the dictionary.
      struct Entry
      {
       public K key;
       public V value;
       public Entry(K key, V value)
       {
        this.key = key;
        this.value = value;
       }
      };
    ...
  8. View code file.
  9. Next to add is the constructor and variables to use with them throughout the business logic and methods, these are mainly used in setting up the dictionary and enabling it to work when in use –
    private Entry[] entries;
    private int numEntries;
    private int maxEntries;
    public GenDictionary(int size)
     {
      entries = new Entry[size];
      numEntries = 0;
      maxEntries = size;
     }
  10. View code file.
  11. Next is our methods for the business logic of the dictionary that has one method to check if the dictionary contains a value and the other will return a value associated with a certain key –
    public bool Contains(K key)
     {
      // Loop through all entries, to find a key that matches the parameter key.
      foreach (Entry entry in entries)
      {
      if (entry.key.Equals(key))
       return true;
      }
      return false;
     }
     public V this[K key]
     {
     ...
     }
  12. View code file.
  13. In Main() now add this code and run the application to see the code in use, maybe add some more employees to the dictionary and try and refer to them from the queue and switch their values
    // Create a dictionary of employees, keyed on their staff number.
     GenDictionary<String, Employee> staff = new GenDictionary<String, Employee>(100);
    // Add some employees.
     staff["007"] = new Employee("James Bond");
     staff["010"] = new Employee("Lee Trundle");
    // Get an employee.
     Employee emp = staff["010"];
    Console.WriteLine("In GenDictionary, Employee 010 is {0}.", emp);
    Console.ReadLine();
  14. View code file.
  15. App

Lab 2: A Closer Look at Generics

Lab 2: A Closer Look at Generics
  1. Here is the formal syntax for class declarations in C#, incorporating support for generics
    attributesopt
    class-modifiersopt class identifier type-param-listopt base-classopt
    type-param-constraints-clausesopt
    {
     class-body
    }

    For example –
    [Serializable]
    public class MyQueue<T> : MyCollection<T>
     where T: struct
    {
     ...
    }
  2. Here are the allowable constraint clauses in C# generics –
    Constraints
  3. Inheritance with generic classes:
    • Consider the following generic class and interface
      public class MySuperClass<T1, T2>
      {...}
      public interface IMyInterface<T>
      {...}
    • View code file.
    • Here are some subclasses that would work with the above –
      // Closed subclass, with specific type parameters.
      public class MySub1 : MySuperClass<int, string>, IMyInterface<int>
      {...}
      // Partially closed subclass, with some specific type parameters.
      public class MySub2<T> : MySuperClass<int, T>, IMyInterface<int>
      {...}
      // Open subclass, with all type parameters still generic.
      public class MySub3<T1, T2> : MySuperClass<T1, T2>, IMyInterface<T1>
      {...}
    • View code file.
  4. Defining Static Fields and Constructors:
    • Static variables in a generic class are shared between all instances of the same “instantiated type” – but not between instances of other instantiated types.
    • Likewise static constructors perform initialization for specific instantiated types– This can be useful to enforce something about the type parameter
      public class MyClass<T>
      {
       private static int count = 0;
       static MyClass()
       {
        if (!typeof(T).IsPrimitive)
         throw new ArgumentException("T must be a primitive type");
       }
       public MyClass() { count++; }
       public static int Count { get { return count; } }
      }
    • View code file.
  5. Overloading Methods in a Generic Type:
    • You can overload methods, constructors, indexers, and operators in a generic type – Possible ambiguities due to various type parameter combinations, compiler always prefers direct-parameter types matches.
    • For example –
      public class MyClassWithOverloading<T>
      {
       // Note: MyClass<string> would have two M1(string) methods.
       // The 2nd would be favoured, because it has an explicit type (not a generic type).
        public void M1(T t)   {...}
        public void M1(string s) {...}
       // OK: no type parameter for T could be string and int simultaneously.
        public void M2(T t1, T t2)   {...}
        public void M2(string s, int i) {...}
       // OK: arrays are different from scalars.
        public void M3(T t)  {...}
        public void M3(T[] a) {...}
      }
    • View code file.
  6. Generics

Lab 3: Generic Structs, Delegates, Interfaces

Lab 3: Generic Structs, Delegates, Interfaces
  1. Generic Structs:
    • Here is the formal syntax for struct declarations in C#, incorporating support for generics
      attributesopt
      modifiersopt struct identifier type-paramsopt interfacesopt type-param-constraintsopt
      {
       struct-body
      }
    • For example –
      [Serializable]
      public struct MyStruct<T> : IMyInterface<T>
       where T: struct
      {
       ...
      }
  2. Generic Interfaces:
    • Here is the formal syntax for interface declarations in C#, incorporating support for generics –
      attributesopt
      modifiersopt interface identifier type-paramsopt base-interfaceopt type-param-constraintsopt
      {
       interface-body
      }
    • For example –
      [ComVisibleAttribute(false)]
      public interface IMyInterface<T> : IMySuperInterface<T>
       where T: struct
      {
       ...
      }
  3. Generic Delegates:
    • Here is the formal syntax for delegate declarations in C#, incorporating support for generics
      attributesopt
      modifiersopt delegate ret-type identifier type-paramsopt(paramsopt) type-param-constraintsopt
    • Example of declaring and using generic delegates –
      public delegate void MyDel<S,C>(S source, C context);
      public class MyClass
      {
       public void B1_OnClick(Button source, EventArgs context) {...}
       public void B2_OnClick(Button source, EventArgs context) {...}
       private MyDel<Button,EventArgs> del1 = new MyDel<Button,EventArgs>(B1_OnClick);
       private MyDel<Button,EventArgs> del2 = new MyDel<Button,EventArgs>(B2_OnClick);
       ...
      }

Lab 4: Generic Methods

Lab 4: Generic Methods

Overview

  1. A generic method is a method that is generic with respect to certain types:
    • Generic methods may appear in generic and non-generic classes, structs, and interfaces.
    • If a generic method appears within a generic class/struct/interface, the generic method can refer to the type parameters of the method and of the containing type
      attributesopt
      method-modifiersopt ret-type method-name type-params (paramsopt) type-param-constraintsopt
      {
       method-body
      }
  2. Example of a generic method, in a non-generic class
    public static class MyUtilClass
    {
     // Search an array, looking for a particular item.
     // Return the index of that item, or -1 if not found.
     public static int FindElement<T> (T[] array, T item)
     {
      for (int i = 0; i < array.Length; i++)
      {
       if (array[i].Equals(item))
        return i;
      }
      return -1;
     }
    }
  3. View code file.
  4. Overloading in generic methods:
    • You can overload generic methods in a type:
      • The rules are similar to overloading methods in a generic type,
        i.e. explicit-type params are favoured over generic-type params.
    • Note:
      • Return types and type parameter names are not significant.
    • For example –
      public class MyClassWithOverloadedGenericMethods
      {
       // Error: the Method1 methods are ambiguous,
       // because return types and type parameter names are not significant.
        public T  Method1<T>(T[] array, T item) {...}
        public void Method1<U>(U[] array, U item) {...}
       // OK: the Method2 methods are not ambiguous,
       // because the number of type parameters is part of the signature.
        public void Method2  (string s);
        public void Method2<T>(string s);
      }
    • View code file.
Lab
  1. A quick example in calling generic methods to show you how they are used so that you will be able to implement them when you need them –
    Form4
  2. Open visual studio, C#>Windows>Console Application and call this CallingGenerics. In program.cs add the new class MyClassToDemoCalls with the code to distinguish between different calls –
    public class MyClassToDemoCalls
     {
      public void Method1(int arg1, T arg2)
      {
       Console.WriteLine("Hello from Method1(int, T) where T is {0}.", typeof(T));
      }
      public void Method1(T arg1, long arg2)
      {
       Console.WriteLine("Hello from Method1(T, long) where T is {0}.", typeof(T));
      }
     }
  3. View code file.
  4. Then add the code to go into Main() that will call this code by creating a new object and sending in different types of data –
    MyClassToDemoCalls obj = new MyClassToDemoCalls();
     obj.Method1(10, 20);
     obj.Method1(10, 20);
    // obj.Method1(10, 20); // This call is ambiguous.
     obj.Method1(10, 20);
     obj.Method1(3.14, 20);
    // obj.Method1(10, 20L); // This call is also ambiguous.
    Console.ReadLine();
  5. View code file.
  6. App4

 

Well done. You have completed the tutorial in the C# course. The next tutorial is

9. Using .NET Framework APIs


Back to beginning
Copyright © 2016 TalkIT®






If you liked this post, please comment with your suggestions to help others.
If you would like to see more content like this in the future, please fill-in our quick survey.
Scroll to Top