Creating and using a generic class or method

Generics is a very interesting way of writing code. Instead of specifying the data type of the elements in the code at design time, you can actually delay the specification of those elements until they are used in code. This basically means that your class or method can work with any data type.

Getting ready

We will start off by writing a generic class that can take any data type as a parameter in its constructor and do something with it.

How to do it…

  1. Declaring a generic class is actually very easy. All that we need to do is create the class with the generic type parameter <T>:
    public class PerformAction<T>
    {
    
    }

    Note

    The generic type parameter is basically a placeholder for a specific type that will need to be defined when the class of variable is instantiated. This means that the generic class PerformAction<T> can never just be used without specifying the type argument inside angle brackets when instantiating the class.

  2. Next, create a private variable of the generic type parameter T. This will hold the value we pass to the generic class:
    public class PerformAction<T>
    {
        private T _value;
    }
  3. We now need to add a constructor to the generic class. The constructor will take as parameter a value of type T. The private variable _value will be set to the parameter passed to the constructor:
    public class PerformAction<T>
    {
        private T _value;
    
        public PerformAction(T value)
        {
            _value = value;
        }
    }
  4. Finally, to complete our generic class, create a void return method called IdentifyDataType(). All that this is going to do is tell us what data type we passed to the generic class. We can find the type of the variable using GetType():
    public class PerformAction<T>
    {
        private T _value;
    
        public PerformAction(T value)
        {
            _value = value;
        }
    
        public void IdentifyDataType()
        {
            WriteLine($"The data type of the supplied variable is {_value.GetType()}");
        }
    }
  5. To see the true beauty of our generic class in action, instantiate the generic class in the console application and specify different data type arguments inside the angle brackets of each new instantiation:
    PerformAction<int> iAction = new PerformAction<int>(21);
    iAction.IdentifyDataType();
    
    PerformAction<decimal> dAction = new PerformAction<decimal>(21.55m);
    dAction.IdentifyDataType();
                
    PerformAction<string> sAction = new PerformAction<string>("Hello Generics");
    sAction.IdentifyDataType();                        
                
    Console.ReadLine();
  6. Running your console application will output the given data types that you instantiated the generic class with each time:
    How to do it…

    We have used the exact same class but let it perform with three very different data types. This kind of flexibility is a very powerful feature in your code.

Another feature of C# is that you can constrain the generic types implemented:

  1. We can do this by telling the compiler that only types that implement the IDisposable interface can be used with the generic class. Change your generic class by adding where T : IDisposable to it. Your generic class should now look like this:
    public class PerformAction<T> where T : IDisposable
    {
        private T _value;
    
        public PerformAction(T value)
        {
            _value = value;
        }
    
        public void IdentifyDataType()
        {
            WriteLine($"The data type of the supplied variable is {_value.GetType()}");
        }
    }
  2. Go back to the console application and have a look at the previous instantiations of the generic class:
    How to do it…

    Visual Studio will tell you that the types underlined by the red squiggly lines do not implement IDisposable and therefore can't be supplied to the PerformAction generic class.

  3. Comment out those lines of code and add the following instantiation to your console application:
    DataSet dsData = new DataSet();
    PerformAction<DataSet> oAction = new PerformAction<DataSet>(dsData);
    oAction.IdentifyDataType();

    Note

    Note that for this to work, you might need to add using System.Data; to your code file. This is needed so that you can declare a DataSet.

  4. As you might know, a DataSet type implements IDisposable, and therefore, it is a valid type to pass to our generic class. Go ahead and run the console application:
    How to do it…

    The DataSet type is valid, and the generic class performs as expected, identifying the type of the parameter passed to the constructor.

But what about generic methods? Well, just like generic classes, generic methods also do not specify their type at design time. It is only known when the method is called. Let's have a look at the following implementation of generic methods:

  1. Let's go ahead and create a new helper class called MyHelperClass:
    public class MyHelperClass
    {
    }
  2. Inside this helper class, we will create a generic method called InspectType. What is interesting about this generic method is that it can return multiple types because the return type is also marked with the generic type parameter. Your generic method does not have to return anything. It can also be declared as void:
    public class MyHelperClass
    {
        public T InspectType<T>(T value) 
        {
            
        }
    }
  3. To illustrate that this generic method can return multiple types, we will output the type passed to the generic method to the console window and then return that type and display it in the console application. You will notice that you need to cast the return type as (T) when returning it:
    public class MyHelperClass
    {
        public T InspectType<T>(T value) 
        {
            WriteLine($"The data type of the supplied parameter is {value.GetType()}");
    
            return (T)value;
        }
    }
  4. In the console application, go ahead and create an enumerator called MyEnum. The generic method can also accept enumerators:
    public enum MyEnum { Value1, Value2, Value3 }
  5. After creating the enumerator, add the following code to the console application. We are instantiating and calling the oHelper class and passing different values to it:
    MyHelperClass oHelper = new MyHelperClass();
    var intExample = oHelper.InspectType(25);
    Console.WriteLine($"An example of this type is {intExample}");
    
    var decExample = oHelper.InspectType(11.78m);
    Console.WriteLine($"An example of this type is {decExample}");
    
    var strExample = oHelper.InspectType("Hello Generics");
    Console.WriteLine($"An example of this type is {strExample}");
    
    var enmExample = oHelper.InspectType(MyEnum.Value2);
    Console.WriteLine($"An example of this type is {enmExample}");
    
    Console.ReadLine();
  6. If you run the console application, you will see that the generic method correctly identifies the type of the parameter passed to it and then returns that type to the calling code in the console application:
    How to do it…

Generic methods can be used in a multitude of situations. This is however only an introduction to generic classes and methods. It is recommended that you do further research to learn how to implement generics in your code appropriately.

How it works…

At the heart of generics lies the ability to reuse a single class or method. It allows developers to essentially not repeat similar code throughout their code base. This conforms well to the Don't Repeat Yourself (DRY) principle. This design principle states that a specific bit of logic should be represented in code only once.

Using generic classes also allows developers to create a class that is type safe when compiling. Type safe basically means that the developer can be assured of the type of the object and can use the class in a specific way without experiencing any unexpected behavior. Therefore, the compiler takes over the burden of type safety.

Generics also allow developers to write less code because code can be reused, and less code also performs better.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.190.217.253