It is fun working with Generics, but sometimes it just won’t work as I intended it to work. While working on a personal project, I was trying to write the code as generic as possible so I can re-use the functions or extension methods in my other projects as well. But it is not always the case, things will not always work as you want it to be. Here is the case of what I was trying to do.
I am working extensively on JSON in this project, so I thought of creating an extension method for the classes I want to serialize to JSON and save it to the file system. Creating the extension method was not a problem, but it turned out to my surprise that I just can’t use the extension method on type T. If I would have paid attention to the detail here before writing the function, I would have noticed the problem. Before I tell you what I did here, here is the extension method which will serialize class to JSON string.
public static string ToJSON<T>(this T Entity) where T : class { return JsonConvert.SerializeObject(Entity); }
As I want only classes to be serialize, I have a constraint in place that assure me that this extension method should be used when the T is of type Class. I agree with the fact the creating an extension method just for a simple work is an over kill but I am just lazy about writing this line again and again. The application I am working on can support multiple databases and for that I am using Dependency Injection (DI). I wrote this function keeping in mind that I just have to pass the model name i.e. class object and this function will then save the JSON string from the serialized class to the file system.
public bool Save<T>(T Entity, string Path) where T : class { bool IsSaved = false; try { if (!Path.IsNullOrEmpty()) { File.AppendAllText(Path, Entity.ToJSON()); IsSaved = true; } return IsSaved; } catch (Exception x) { throw x; } }
The above code will not compile. This is because, there is no way for the compiler to know the type of Entity
object we are passing. The constraints has nothing to do with it. It is just the compiler who has to know what kind of type it is dealing with. In this case we have a type T which is not resolved at compile time and hence the ToJSON()
extension method I am using above, is an assumption from my part that Entity
is a class, is not going to work. The important point here is that T is going to resolve at runtime and before that if we try to use the extension method (meant only for class) on type T, the intellisense will never show the ToJSON()
extension method in the list.
Now to overcome this problem, we have to make some changes to the above code in order to have the information which the compiler can use and allow us to make use of the ToJSON()extension method. As there is a constraint in place for this function which will only allow us to pass class object as a first parameter, but the case is still the same. The Save
function will never know what is the actual type of the Entity
. Therefore we can use the Entity object and get the name of the Class like so.
string className = Entity.GetType().Name;
This gives us the name of the class and now we can use it to get the actual type of T and convert it to that type (which will be class). Once we have a class we can then use our ToJSON() extension method. Here is the updated code.
public bool Save<T>(T Entity, string Path) where T : class { bool IsSaved = false; try { if (!Path.IsNullOrEmpty()) { if (Entity.GetType().Name == "Board") File.AppendAllText(Path, Convert.ChangeType(Entity, typeof(Board)).ToJSON()); else if (Entity.GetType().Name == "Story") File.AppendAllText(Path, Convert.ChangeType(Entity, typeof(Story)).ToJSON()); IsSaved = true; } return IsSaved; } catch (Exception x) { throw x; } }
This code will compile and saves the JSON string to the specified path on execution. Notice the Convert.ChangeType
function. It’s simple function which you can use to convert one type to your desired type. The only drawback here will be that you have to come here and update the function whenever you have a type of class added in the project which you want to serialize to JSON. The code is still pretty sleek according to me for the type of task I am trying to accomplish.
The type T is very useful, but sometimes it may not solve all your problem.