Saturday, May 30, 2015

.NET Generic Overloads: How to support T and IList

What happens when you want to overload a generic method where the first method accepts a single object and the other accepts an IList of that type?

It will only work when specifically try to pass in an IList. If you try to pass in a List it will fail because the compiler will identify the generic parameter overload and fail before trying to use the implicit cast to an IList.

This is because a C# compiler tries to identify overloaded methods it checks for matching parameters in the following order:

  1. Explicit type matches.
  2. Generic parameters.
  3. Implicit type matches.

Let's look at an example!

Example of what DOES NOT work.

public class OverloadSample1
{
    [Fact]
    public void Test()
    {
        // Matches Method A - Good
        ISample sample = new Sample();
        this.Method(sample);
 
        // Matches Method B - Good
        IList<ISample> samples1 = new List<ISample>();
        this.Method(samples1);
 
        // Matches Method A - BAD!
        List<ISample> samples2 = new List<ISample>();
        this.Method(samples2);
    }
 
    // A
    public void Method<T>(T sample)
        where T : ISample
    {
    }
 
    // B
    public void Method<T>(IList<T> sample)
        where T : ISample
    {
    }
}

...so, how do we solve this problem?

Using extension methods to solve the problem.

The compiler searches for method options it checks the class implementation in the order described above, and THEN it will look for extension methods in the same order. So one solution to our problem is to expose the generic match as an extension, thus forcing the compiler to check for an implicit cast first.

Let's see this workaround in action!

public class OverloadSample2
{
    [Fact]
    public void Test()
    {
        // Matches Method A - Good
        ISample sample = new Sample();
        this.Method(sample);
 
        // Matches Method B - Good
        IList<ISample> samples1 = new List<ISample>();
        this.Method(samples1);
 
        // Matches Method B - Good
        List<ISample> samples2 = new List<ISample>();
        this.Method(samples2);
    }
 
    // C
    internal void MethodSingle<T>(T sample)
        where T : ISample
    {
    }
 
    // B
    public void Method<T>(IList<T> sample)
        where T : ISample
    {
    }
}
 
public static class OverloadSample2Extensions
{
    // A
    public static void Method<T>(this OverloadSample2 overloadSample, T sample)
        where T : ISample
    {
        overloadSample.MethodSingle(sample);
    }
}

Enjoy,
Tom

No comments:

Post a Comment

Real Time Web Analytics