Archive for April, 2010

C# 4.0 Covariant Generic Interfaces + Extension Methods == Very Cool!

April 12, 2010

I ran into something at work involving a conversion routine that I had written about a year ago that takes a list of a type and converts it into a string. Today I had a new class with very similar fields that I needed to run the same coversion on. I was able to work it out in C# 3, but not quite in an ideal way. To do it the right way I’d have to be using C# 4 because if its support for covarance in interfaces, which, as it turns out, makes extension methods much more useful.

Here’s a (rather silly) example that demonstrates a situation similar to mine:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CovariantExtensionMethods
{
    public interface IMyInterface
    {
        string Value { get; set; }
    }

    public class MyClass : IMyInterface
    {
        public string Value { get; set; }
    }

    public class MyOtherClass : IMyInterface
    {
        public string Value { get; set; }
    }

    public static class MyExtensions
    {
        public static string ConvertStuff(this IEnumerable<IMyInterface> stuff)
        {
            return string.Join(", ", stuff.Select(s => s.Value));
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            List<MyClass> mine = new List<MyClass>
            {
                new MyClass { Value = "One" },
                new MyClass { Value = "Two" },
                new MyClass { Value = "Three" },
            };

            List<MyOtherClass> mine2 = new List<MyOtherClass>
            {
                new MyOtherClass { Value = "One" },
                new MyOtherClass { Value = "Two" },
                new MyOtherClass { Value = "Three" },
            };

            Console.WriteLine(mine.ConvertStuff());
            Console.WriteLine(mine2.ConvertStuff());
        }
    }
}

So what’s going on here? First, I have an interface, IMyInterface, that defines a single property, Value, and a class that implements that interface. Then I have an extension method, ConvertStuff, that takes an IEnumerable<IMyInterface>. In the Main method I then have a List<MyClass>, and a List<MyOtherClass>, and I call the ConvertStuff extension method on both lists – even though they are lists of different types!

In the past, this extension method would have only shown up on a variable typed as an IEnumerable<IMyInterface> (or one of its subclasses, such as List<IMyInteface>), but not on List<MyClass>, but because in .NET 4 IEnumerable now supports covariance (its definition is IEnumerable<out T>) my extension method will show up on any class that implements IEnumerable<[IMyInterface or any of its implementations]>.  This is very similar to what you’d see in Java as IEnumerable<? implements IMyInterface>, but it just happens automatically in C# 4 because the covariance is defined by the writer of the type, and not the consumer.

This makes extension methods much more versatile, and is a very welcome addition to C#.

Thanks Microsoft!


Follow

Get every new post delivered to your Inbox.