Extension methods in C# .NET part 2: arrays, collections, portability

Introduction

We’ll continue our discussion of extension methods by looking at arrays, collections and other types. We’ll also discuss portability issues.

You can find the previous part in this series here.

Arrays and collections

Open up the demo project we’ve been working on in this series. Locate the ExtensionSamples project and add a new folder called Collections. We’ll reuse much of the code we added to the Interfaces folder. Here we want to introduce another search functionality similar to the SearchProduct extension which searched through one specific data source. We now want to search through all data sources in one call. Add a class called ProductCollectionExtensions to the Collections folder and make it public and static. Make sure that the namespace is the same as for the IProductDataSource interface to avoid having to copy namespaces. Add the following extension method stub:

public static IEnumerable<Product> GetAllItemsByName(this IProductDataSource[] productDataDources, string searchTerm)
{
	return null;
}

You can see that we’re extending an array of product data sources. We want to search through all data sources and find a product with some name in all those sources.

A method call from Main looks like this:

private static void TestCollectionExtensions()
{
	IProductDataSource[] dataSources = new IProductDataSource[] { new DatabaseSource(), new FileSource(), new ApiSource() };
	string searchTerm = "f";

	IEnumerable<Product> productsWithId = dataSources.GetAllItemsByName(searchTerm);
}

In the method implementation we can reuse the already existing SearchProduct extension on each data source in an iteration:

public static IEnumerable<Product> GetAllItemsByName(this IProductDataSource[] productDataDources, string searchTerm)
{
	List<Product> products = new List<Product>();
	foreach (IProductDataSource productDataSource in productDataDources)
	{
		products.AddRange(productDataSource.SearchProduct(searchTerm));
	}
	return products;
}

Yet again we extended the functionality of IProductDataSource without having to touch the code in the concrete classes.

This was an example of how to write extensions for arrays. The same applies for all types of sequences, such as IEnumerable, List, IList etc. The GetAllItemsByName method can equally be written as follows:

public static IEnumerable<Product> GetAllItemsByName(this IEnumerable<IProductDataSource> productDataDources, string searchTerm)

The code will still compile as an array is also an IEnumerable so the change in the signature doesn’t break the existing code. You can call the GetAllItemsByName on any collection that implements the IEnumerable interface such as a List of T or HashSet of T.

Extending any object

What if you want to make an extension method available on just any type of object? It’s certainly possible. There’s nothing stopping you from declaring the ‘this’ parameter on the built-in type Object. There are scenarios where this is feasible, such as cross-cutting concerns: logging, caching and the like.

Add a new folder in the ExtensionExamples project called Objects. Add a new class to it called ObjectExtensions and declare it public and static. Make sure that the namespace is System. This will ensure that the extension will be available everywhere throughout the solution as System is referenced by default. In general you can restrict the IntelliSense visibility of an extension method by controlling its namespace.

Let’s say we want to be able to get the XML representation of any object for logging purposes. Add the following – admittedly very primitive – implementation to the object extensions class:

public static string ToXmlRepresentation(this object target)
{
	return string.Concat("<xml>", target.ToString(), "</xml>");
}

An exact implementation is beside the point here. The extension method can be called from Main like this:

private static void TestObjectExtensions()
{
	Product p = new Product() { Id = 1, Name = "product" };
	int i = 5;
	string s = "hello";
        DateTime now = DateTime.UtcNow;
	string xmlProduct = p.ToXmlRepresentation();			
	string integerXml = i.ToXmlRepresentation();
	string stringXml = s.ToXmlRepresentation();
        string dateXml = now.ToXmlRepresentation();
}

The extension method will be available on all elements that derive from the Object class: integer, string, built-in objects, interfaces, your custom objects etc.

Types

We can extend Types as well. Add a new class called TypeExtensions to the Objects folder. Make it public and static and change the namespace to System. Let’s say we need some metadata of any Type in .NET:

public static string GetTypeMetadata(this Type target)
{
	return string.Concat("Assembly: ", target.AssemblyQualifiedName, ", name: ", target.FullName);
}

From Main:

private static void TestTypeExtensions()
{
	Product p = new Product() { Id = 1, Name = "product" };
	int i = 5;
	string s = "hello";
	DateTime now = DateTime.UtcNow;
	string productMetadata = p.GetType().GetTypeMetadata();
	string integerMetadata = i.GetType().GetTypeMetadata();
	string stringMetadata = s.GetType().GetTypeMetadata();
	string dateMetadata = now.GetType().GetTypeMetadata();
}

Limitations

You can achieve a lot with extension methods but not everything. In particular you cannot extend the state or accessibility of an object. As extension methods are defined in a separate public static class only those properties and methods will be available to the extension method that are accessible to the class holding the extension methods. You cannot of course reference private properties of an object you’re trying to extend. This is the normal and expected behaviour, extension methods are no exception to the accessibility rules.

You cannot create new instances of an extension method class as it is static. You cannot add instance level fields or properties to it for the same reason. You can certainly add private static fields to an extension method class and maintain some state in them for the incoming object instance. However, as it is a static field it won’t ever be collected by the garbage collector. It will just keep growing.

If you absolutely need to access a private field of the object you’re extending then Reflection can come to the rescue. Imagine that our Product class has a private field with a default value:

private string _privateField = "default";

We can have an extension method like this:

public static string DoSomethingWithPrivateField(this Product p)
{
	FieldInfo fieldInfo = typeof(Product).GetField("_privateField", BindingFlags.Instance | BindingFlags.NonPublic);
	string privateFieldValue = (string)fieldInfo.GetValue(p);
	return privateFieldValue;
}

This means that Reflection will look for a private instance variable called “_privateField” on a Product object. Then we call GetValue and specify that it should be the incoming “p” where the private field should be found.

Call it from Main:

private static void TestPrivateField()
{
	Product p = new Product();
	string privateField = p.DoSomethingWithPrivateField();
}

There you have it, the extension method will find the value of the private field. However, keep in mind that reflection is “expensive” so don’t overuse it. Also, there’s no guarantee that the name of the private field won’t change in the future and the code may break at any point in the future.

Therefore it’s not recommended to use private static variables to keep track of the state or reflection to read private variables from an object. Use them only if there’s no other solution and use them with special care.

Extension methods can obviously only extend objects that are visible to the containing class, such as other public classes. You cannot extend private classes of course or any other class that’s not visible from another class. This is not surprising: extension methods are very much like normal methods and the same basic accessibility rules apply to them as to any other class.

If you create an extension method with the same name as a method that already exists in the class then the compiler will call the class definition. In other words extension methods have a lower priority than class methods and there’s no way around that rule. All you can do is rename the extension method. This can be confusing at first because you won’t get any compiler error or runtime exception but your extension method will not be called.

If you have two extension methods with the same name that extend related types of objects, such as IEnumerable of String and List of String then the extension method with the most specific match will be called. In that case if I call (List of String).ConvertToSomethingElse() then the List of String extension will be called and not IEnumerable of String if you have the following methods:

ConvertToSomethingElse(this List<String> list);
ConvertToSomethingElse(this IEnumerable<String> list);

The same is true if some object implements an interface:

ConvertToSomethingElse(this int integer);
ConvertToSomethingElse(this IConvertible list);

Integer implements the IConvertible interface. So if we call (some integer).ConvertToSomethingElse then both extension methods could apply. The compiler will select the one that extends the Integer type as it is the closest match. If you cast the integer to IConvertible…:

((IConvertible)input).ConvertToSomethingElse();

…then the IConvertible version will be called.

One more limitation, which is not really a limitation but normal behaviour, is that if you define two extension methods with the same signature in the same namespace but different files then the compiler will complain. It’s because it cannot select the correct one. You get the same compiler error if you define two “normal” methods with the same signature in the same class: the compiler will complain about ambiguous references. Keep this in mind when changing the namespace to something too wide, like “System” as some other programmer working on the same project may do the same and there’s a risk of declaring the same signatures leading to a compiler error.

Keep in mind that extension methods are really just syntactic sugar added to .NET. They add functionality to the objects they extend without changing the object definition in any way. This explains why we have the above limitations.

Portability

Extension methods are portable to a high degree with some tricks:

  • Visual Basic projects can call extension methods written in C# as long as there’s no .NET profile mismatch
  • Using an extension method written in .NET4.5 C# library cannot be used directly in a project with no full .NET, e.g. a Windows Phone project with a specific runtime. You can only reference an extension method contained in a Portable Class Library project from a Windows 8/Phone app. When you create a Portable Class Library you can select which frameworks to support: WP8, Windows Store apps, Silverlight 5 and .NET4 and above. Creating an extension method in a portable class library follows exactly the same process as we’ve seen so far. Portable libraries can be referenced from non-portable libraries as well.
  • Extension methods written in .NET4.5 cannot be used in assemblies that target a lower .NET version or a limited profile, like the Client profile. In this case not even a portable client library can be used. In that case what you can do is create a separate C# library, retarget it to a lower .NET framework or profile, such as .NET3.5 Client Profile, and add the extension method in the original C# library as a link: Add Existing Item…, navigate to the cs file containing the extension methods. Before clicking Add in the Add Existing Item window click the little downward pointing triangle on the right side of the Add button. You’ll see two options there: Add and Add Link. Select Add Link. This will at least ensure that the same source code is shared between the original C# library and the one targeting a lower .NET version. Keep in mind that .NET3.5 is the earliest framework where you can use extension methods.

Read the last post in this series here.

Advertisements

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 )

Google photo

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

ultimatemindsettoday

A great WordPress.com site

Elliot Balynn's Blog

A directory of wonderful thoughts

Softwarearchitektur in der Praxis

Wissenswertes zu Webentwicklung, Domain-Driven Design und Microservices

Technology Talks

on Microsoft technologies, Web, Android and others

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT BEST PRACTICES WITH MICROSOFT STACK & ANGULAR

Cyber Matters

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

Guru N Guns's

OneSolution To dOTnET.

Johnny Zraiby

Measuring programming progress by lines of code is like measuring aircraft building progress by weight.

%d bloggers like this: