Pranay Rana: Return Given Type from Generic method

Saturday, January 19, 2019

Return Given Type from Generic method

In C#, Genetic method as below, which can return value of multiple types (i.e. below method can return value of any primitive types or reference types).
   public T GetValue<T>()
   {
  
   }
so one cannot return value directly from method. As T is template type & it can be any of type, so compiler doesn't allow direct conversation of return value to type T.
   public T GetValue<T>()
   {
     //Compilation error (line 13, col 10): Cannot implicitly convert type 'Customer' to 'T'
     return new Customer();
     //this error says that T is template type and it can be of any type (primitive or reference)
   }
Above will not work because Template type of Generic method get replaced at Runtime and Compiler of C# cannot able to figure out Generic type actual type at compile time. for example call to method is like as below
// call to method
GetValue<int>()

//then at runtime method will be like as below 
public int GetValue<int>()
{
 //assuming generic method return customer object only 
 return new Customer();
}

Solution 
As explained in detail problem with generic method is to return value of requested return type (i.e. Runtime type which replaces generic Template type T). So to solve problem return value to object type(as object is base class of all types in C# .net) and then convert it to generic template type. Below is code for that solves problem
public T GetValue<T>()
{
  Type typeParameterType = typeof(T);
    Type typeParameterType = typeof(T);
  if(typeParameterType.ToString()=="System.Int32")
    return (T)(object)1;
   if(typeParameterType.ToString()=="Customer")
    return (T)(object)new Customer()
}

But problem with above code is ,for every new return type value new condition required to be added in method i.e it requires to change method for every new return type and it violates Single responsibility principle.

This can easily be resolved by making use of C# in built method Convert.ChangeType, below is code for the same
  (T)Convert.ChangeType(1, typeParameterType)//here T is generic type

Below is code for getting value of column form DataRow object which is based on above discussion
  public static T GetColumnValue<T>(this DataRow dr, string columnName)
  {
     //get type of Template type T
     Type typeParameterType = typeof(T);
     //get type of Template type T, this works when requested type is nullable
     typeParameterType = Nullable.GetUnderlyingType(typeParameterType) ?? typeParameterType;

     //check value present or not , if not return default value of generic type
     return dr[columnName] != DBNull.Value
                ? (T)Convert.ChangeType(dr[columnName], typeParameterType)
                : default(T);
   } 
//use of this method in code 

datarow.GetColumnValue<int>("id");
datarow.GetColumnValue<int?>("orderid");
datarow.GetColumnValue<double>("amount");

so above is Generic method to get value of datacolumn of given type. Method get value of return type from template type T and then does conversation of datacolumn value using Convert.ChangeType.

Below is example of Factory Design pattern using same technique
public class CommandFactory
{       
    //only create object of type which implement ICommand
    public static T GetCommand<T>() where T:  ICommand
    {
      //it create object using Template type T and by making use of reflection
      //once create factory this way there is no need to open it again for modification
        Type type = typeof(T);
        Object obj = Activator.CreateInstance(type);
        return (T)obj;
    }
}

Read about this in detail here : Factory Design Pattern With Generics

No comments:

Post a Comment