Friday, November 18, 2016

Brain Teaser - Generics

In the previous post we had optimised the code using Inheritance, in current post we will see how we can optimise previous solution using Generics. Lets first understand what is Generics in .Net. Generics are introduced in C# 2.0 and it allows us to create data structures which are decoupled from data types. In simple words you can say Generics is a technique of writing the code for a class without specifying the data type(s) that the class works on. The data type is specified only when you declare an instance of a generic class. This allows a generic class to be specialised for many different data types while only having to write the class once. The best example of Generics is .NET collection classes (System.Collections.Generic) where Generics is being used heavily. You can also create your own generic interfaces, classes , methods, events and delegates. Well Generics can be applied to:
  • Interface
  • Abstract class
  • Class
  • Method
  • Static method
  • Property
  • Event
  • Delegates
  • Operator
Syntax for defining a Generic Class or Method: Use angular brackets  <>  to define the generic classes, methods, properties, events, delegates with type parameter. You can use any alphabet to specify the type parameter, but the best practice says you should use T for specifying the type parameter.. If you have more than one type parameter prefix descriptive type parameter names with "T".
class ClassName<T> {}
void MethodName<T>(T parameter) {}
class ClassName<TKey, TValue> {}

Advantages of using Generics:
  • Maximises code reuse
    • Generic makes the code (classes, methods, interface, delegate) independent of data types. So the same piece of code can be used for N numbers of types which poses the same behaviours. Example below method can be used both with string as well as int.
                         bool AreEqual<T>(T value1, T value2)
                         {
                                return value1.Equals(value2);
                          }
    •  Like methods, classes, interfaces and delegates can be made generic
                            public class MyGenericClass<T>
                           {
                                 public bool AreEqual(T value1, T value2)
                                {
                                       return value1.Equals(value2);
                                }
                            }
  • Type safety
    • Generics are strongly typed, you are notified during compile time if you try to use different data type of data than the specified in the definition. See the below code which uses the Generic class used in the above example.
                          MyGenericClass<int> c = new  MyGenericClass<int> (); //definition
                          c.AreEqual(1, "test"); // this line of code is gonna give compile time error.
  • Performance Improvement
    • Boxing (converting value type to reference type) and un-boxing (converting reference type to value type) overhead is no longer there. 

I will be covering Generics in detail in my future posts. So for now lets move on to our code optimisation task. 
  • Move the methods which has same name but different implementation in Audi and Benz classes as below:

             public interface ICar
            {
                  void Move();
                  void ApplyBreak();
            }  
  • Leave the common code in Vehicle class and make it a generic class as below:
            public class Car<T> where T: class, ICar, new()
           {
                 private T car;
                 public Vehicle()
                {
                    car = new T();
                }

                 protected void Start()
                {
                       Console.WriteLine("Car Started");
                 }

                public void Drive()
               {
                     Start();
                     car.Move();
                     car.ApplyBreak();
               }    
           }
  • Make Benz and Audi class implement the interface created above IVehicle as below:
             public class Benz :ICar
            {
             
                 //Benz specific functionality
                 public  void Move()
                {
                         Console.WriteLine("Moving with the power of a 3000 CC Diesel engine");
                }

                //Benz specific functionality
              public void ApplyBreak()
             {
                   Console.WriteLine("Applied Power break with ABS");
             }
          }

        public class Audi: ICar
       {
              //Audi specific functionality
             public void Move()
            {
                Console.WriteLine("Moving with the power of a 2000 CC Pterol engine");
            }

             //Audi specific functionality
             public void ApplyBreak()
            {
                 Console.WriteLine("Applied Power break with sensors");
            }
       }

  • Now view the output:
           class Program
           {
                static void Main(string[] args)
               {
                   Car<Audi>  a = new Car<Audi>();
                   a.Drive();

                   var b= new Car<Benz>();
                   b.Drive();

                  Console.Read();
               }
           }

No comments:

Post a Comment