Introduction to generics in C# Part 5

Introduction

In the previous post we looked at how to declare multiple type parameters. We saw that it was a very simple thing to do. We just add as many parameters as we need and separate them by a comma. It’s perfectly fine to refer to those parameters later on in the class level functions. It’s equally fine to put multiple parameters on the function level as well if you don’t want to make the entire class generic.

In this post we’ll take a quick look at parameter type constraints.

Constraints

So far we’ve only seen examples where we could declare just any type of parameter type, like here:

public interface IConfigurationRepository
{
	T ReadConfigurationValue<T>(string key, T defaultValue, bool throwException = false);
}

We can potentially pass in just any object type for ‘T’: a string, a Customer, a GUID, etc. However, there are occasions where we want to be able to constrain the types that the generic method or class can handle.

Consider a situation where you’d like to print the properties of an object. There are various ways to implement a simple properties printer but we’ll look at a – somewhat artificial – solution where we can demonstrate the subject of this post. The object that needs printing must implement the following interface:

public interface IPrintable
{
	IDictionary<string, string> GetProperties();
}

So the object needs to expose its properties in a simple string dictionary. Let’s revisit the Customer object we saw before in this series. The Customer class implements the interface as follows:

public class Customer : Domain<int>, IPrintable
{
	public Customer(int id, string name)
		: base(id)
	{
		Name = name;
	}

	public string Name { get; }

	public IDictionary<string, string> GetProperties()
	{
		IDictionary<string, string> props = new Dictionary<string, string>();
		props["Name"] = Name;
		props["Id"] = Convert.ToString(Id);
		props["ObjectType"] = "Customer";
		return props;
	}
}

The next step is to decide what format we want to follow: XML, JSON, CSV or some other, possibly custom format. There are multiple printer implementations so we should create an interface. However, we want to make sure that only those objects can use this interface that implement the IPrintable interface. We want to do that since we want to make sure that there will be GetProperties() method for the generic type T.

That’s an example of a constraint. We create a generic type but we limit the usage of the type to those objects that fulfil some criteria: they implement an interface, they derive from a certain class and the like. The ‘where’ keyword is used for this purpose. Here’s the IPropertiesPrinter interface:

public interface IPropertiesPrinter<T> where T : IPrintable
{
	string Print(T toBePrinted);
}

The where clause means that type T must implement the IPrintable interface otherwise the Print method cannot be called on it.

Let’s see two implementations, one for JSON and one for XML:

public class JsonPropertiesPrinter<T> : IPropertiesPrinter<T> where T : IPrintable
{
	public string Print(T toBePrinted)
	{
		string ret = JsonConvert.SerializeObject(toBePrinted.GetProperties());
		return ret;
	}
}

public class XmlPropertiesPrinter<T> : IPropertiesPrinter<T> where T : IPrintable
{
	public string Print(T toBePrinted)
	{
		string root = typeof(T).ToString();
		XDocument xDocument = new XDocument
		(
			new XDeclaration("1.0", "utf-8", null),
			new XElement(root, toBePrinted.GetProperties().Select(kvp => new XElement(kvp.Key, kvp.Value)))
		);
		string ret = xDocument.ToString();
		return ret;
	}
}

Let’s see how we can call these implementations:

Customer customer = new Customer(123, "Great customer");
IPropertiesPrinter<Customer> printer = new JsonPropertiesPrinter<Customer>();
string printed = printer.Print(customer);

The variable ‘printed’ will be…:

{"Name":"Great customer","Id":"123","ObjectType":"Customer"}

Whereas the XML printer…

Customer customer = new Customer(123, "Great customer");
IPropertiesPrinter<Customer> printer = new XmlPropertiesPrinter<Customer>();
string printed = printer.Print(customer);

…produces the following output:

<Various.Generics.Customer>
  <Name>Great customer</Name>
  <Id>123</Id>
  <ObjectType>Customer</ObjectType>
</Various.Generics.Customer>

Attempting to use the IPropertiesPrinter interface with an object that doesn’t implement the IPrintable interface will result in a compiler error.

You can find out more about the options for the where clause in this MSDN article.

Read the next post here which also concludes this series.

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.

One Response to Introduction to generics in C# Part 5

  1. Atilla Selem says:

    very good sample use-cases. -) thx to ur mother!

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 )

Twitter picture

You are commenting using your Twitter 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: