Introduction to generics in C# Part 2

Introduction

In the previous post we started looking into generics in .NET. We also saw an initial example where an abstract base class had a type parameter to define the type of the Id property. The implementing classes could then all derive from this generic class and define their Id type along the way.

In this post we’ll look at another example where generics can be useful.

A second example: application settings

A typical .NET application has some kind of application configuration file, most often a web.config or app.config depending on the project type. The XML-based config file has a section called appSettings where you can add your custom settings as key-value pairs like in the following example:

<configuration>
	<appSettings>
		<add key="environment" value="alpha"/>
		<add key="max-loops" value="10"/>
		<add key="cheat-mode-on" value="true"/>
	</appSettings>
	
</configuration>

On a side note: in the new ASP.NET core the traditional XML-based config file is replaced by a JSON file.

Note that all values are stored as strings in the XML file of course. However, we want to convert them into their real types: string, int and boolean respectively. There are various ways to solve the problem but I think generics provides the neatest.

We can read a single setting from the config file as follows:

string key = "environment";
string value = ConfigurationManager.AppSettings[key];

The ConfigurationManager class is located in the System.Configuration namespace. It will only be available if you add a reference to the System.Configuration DLL in your project. The above code is probably as simple as it gets: there’s no type conversion, no initial validation that there’s an entry by that key in the settings file etc. Let’s put that into some service:

public class ConfigurationReaderService
{
	public string ReadConfigurationValue(string key)
	{
		return ConfigurationManager.AppSettings[key];
	}
}

Here’s how we could call that service and read the max-loops setting:

ConfigurationReaderService configService = new ConfigurationReaderService();
string key = "max-loops";
string maxLoopsRaw = configService.ReadConfigurationValue(key);
if (!string.IsNullOrEmpty(maxLoopsRaw))
{
	int maxLoops;
	if (int.TryParse(maxLoopsRaw, out maxLoops))
	{
		Debug.WriteLine(string.Format("Converted: {0}", maxLoops));
	}
	else
	{
		Debug.WriteLine(string.Format("Could not convert: {0}", maxLoopsRaw));
	}
}
else
{
	Debug.WriteLine(string.Format("There's no such setting: {0}", key));
}

The code writes “Converted: 10” in the debug window. If we change the value of “max-loops” to a string that cannot be converted into an int, such as “ElvisPresley”, then we get “Could not convert: ElvisPresley”. If we remove or rename the setting “max-loops” from the config file then we get “There’s no such setting: max-loops”.

That might actually be all you need: call that simple service and deal with the string variable afterwards. However, I think a configuration reader service should be a bit more versatile. Ideally it would be flexible to be able to perform the type conversion on its own so that the caller can focus on the converted value directly. This is so that the caller can avoid having to go through all those validation steps.

Let’s see what a solution with a class type parameter could look like:

public class ConfigurationReaderService<T>
{
	public T ReadConfigurationValue(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;
		}
	}
}

The above solution lets us define the type of the value, a default value and also whether an exception should be thrown if the setting is not found by the given key or if the conversion fails. If no exception is thrown and the conversion process fails somewhere then we return the default value. The type conversion even handles enumerations.

Let’s see how we can use this new service from the caller:

ConfigurationReaderService<int> intConfigService = new ConfigurationReaderService<int>();
int maxLoops = intConfigService.ReadConfigurationValue("max-loops", 50);
Debug.WriteLine(string.Format("Max loops: {0}", maxLoops));

ConfigurationReaderService<bool> boolConfigService = new ConfigurationReaderService<bool>();
bool cheatModeOn = boolConfigService.ReadConfigurationValue("cheat-mode-on", false);
Debug.WriteLine(string.Format("Cheat mode on: {0}", cheatModeOn));

Here’s the output:

Max loops: 10
Cheat mode on: True

I think this is a great improvement compared to the first solution where the caller had to do a lot of heavy lifting.

However, it feels futile that we have to declare a new ConfigurationReaderService for every type we want to extract from the config file. How can we remedy that? We’ll see it tomorrow in the next part.

View all various C# language feature related posts here.

Advertisement

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.

%d bloggers like this: