Introduction to generics in C# Part 3
March 4, 2016 Leave a comment
Introduction
In the previous post on generics we looked at an example of reading application settings from a configuration file. Application settings come in key-value pairs where the value can be of different type: string, integer, boolean and various others. The generic ConfigurationReaderService helped us improve the non-generic code where the function caller had to take care of the type conversion and validation steps.
In this post we’ll further improve our code by looking into generic methods.
Generic methods, generic functions
So far we in this short series we have looked at generic classes, such as the one from the previous post:
public class ConfigurationReaderService<T> { }
We put the type parameter on the class declaration. Any time you construct a new ConfigurationReaderService you have to define its type parameter, like here:
ConfigurationReaderService<int> intConfigService = new ConfigurationReaderService<int>(); ConfigurationReaderService<bool> boolConfigService = new ConfigurationReaderService<bool>();
…depending on the type of the setting you’d like to retrieve. However, it feels a bit awkward to construct a new ConfigurationReaderService instance whenever we need to change the type parameter.
Fortunately for us we don’t need to set the entire class generic. We can make individual methods and functions generic as well using the same technique as how we attached the type parameter after the class declaration. We just need to do it on the function level instead.
The change on the ConfigurationReaderService class is minimal:
public class ConfigurationReaderService { public T ReadConfigurationValue<T>(string key, T defaultValue, bool throwException = false) { var value = ConfigurationManager.AppSettings[key]; if (value == null) { if (throwException) { throw new KeyNotFoundException("Key " + key + " not found."); } return defaultValue; } try { if (typeof(Enum).IsAssignableFrom(typeof(T))) { return (T)Enum.Parse(typeof(T), value); } return (T)Convert.ChangeType(value, typeof(T)); } catch (Exception ex) { if (throwException) { throw ex; } return defaultValue; } } }
We just put the type parameter after the function name instead of the class name, that’s it.
Now it’s enough to construct one ConfigurationReaderService instance and call its ReadConfigurationValue multiple times:
ConfigurationReaderService configService = new ConfigurationReaderService(); int maxLoops = configService.ReadConfigurationValue<int>("max-loops", 50); Debug.WriteLine(string.Format("Max loops: {0}", maxLoops)); bool cheatModeOn = configService.ReadConfigurationValue<bool>("cheat-mode-on", false); Debug.WriteLine(string.Format("Cheat mode on: {0}", cheatModeOn));
This produces the same output in the debug window as before:
Max loops: 10
Cheat mode on: True
While we’re at it we can demonstrate that even interfaces can have generic functions:
public interface IConfigurationRepository { T ReadConfigurationValue<T>(string key, T defaultValue, bool throwException = false); }
Our ConfigurationReaderService class can then implement this interface:
public class ConfigurationReaderService : IConfigurationRepository
In the next part we’ll look into setting multiple type parameters on the class or function.
View all various C# language feature related posts here.