I have the following two generic types:
interface IRange<T> where T : IComparable<T>
interface IRange<T, TData> : IRange<T> where T : IComparable<T>
^---------^
|
+- note: inherits from IRange<T>
Now I want to define an extension methods for collections of these interfaces, and since they're both either IRange<T>
or descends from IRange<T>
I was hoping I could define one method that would handle both. Note that the method will not need to deal with any of the differences between the two, only the common part from IRange<T>
.
My question is thus this:
Can I define one extension method that will handle collections (IEnumerable<T>
) of either of these two types?
I tried this:
public static void Slice<T>(this IEnumerable<IRange<T>> ranges)
where T : IComparable<T>
however, passing an IEnumerable<IRange<Int32, String>>
, like this:
IEnumerable<IRange<Int32, String>> input = new IRange<Int32, String>[0];
input.Slice();
gives me this compiler error:
Error 1 'System.Collections.Generic.IEnumerable>' does not contain a definition for 'Slice' and no extension method 'Slice' accepting a first argument of type 'System.Collections.Generic.IEnumerable>' could be found (are you missing a using directive or an assembly reference?) C:\Dev\VS.NET\LVK\LVK.UnitTests\Core\Collections\RangeTests.cs 455 26 LVK.UnitTests
Note: I did not expect it to compile. I know enough about co(ntra)-variance (some day I need to learn which one is which way) to know that won't work. My question is if there's anything I can do to the Slice declaration to make it work.
Ok, so then I tried to infer the type of the range interface, so that I could handle all types of IEnumerable<R>
as long as the R
in question was a IRange<T>
.
So I tried this:
public static Boolean Slice<R, T>(this IEnumerable<R> ranges)
where R : IRange<T>
where T : IComparable<T>
This gives me the same problem.
So, is there a way to tweak this?
If not, are my only options to:
- Define two extension methods, and call an internal method internally, perhaps by converting one of the collections to one that contains the base interface?
- Wait for C# 4.0?
Here's how I envision defining the two methods (note, I'm still in the early design phases of this, so this might not work at all):
public static void Slice<T>(this IEnumerable<IRange<T>> ranges)
where T : IComparable<T>
{
InternalSlice<T, IRange<T>>(ranges);
}
public static void Slice<T, TData>(this IEnumerable<IRange<T, TData>> ranges)
where T : IComparable<T>
{
InternalSlice<T, IRange<T, TData>>(ranges);
}
private static void Slice<T, R>(this IEnumerable<R> ranges)
where R : IRange<T>
where T : IComparable<T>
Here's a sample program code that shows my problem.
Note that by changing the calls from Slice1 to Slice2 in the Main method makes both usages produce compiler errors, so my second attempt didn't even handle my initial case.
using System;
using System.Collections.Generic;
namespace SO1936785
{
interface IRange<T> where T : IComparable<T> { }
interface IRange<T, TData> : IRange<T> where T : IComparable<T> { }
static class Extensions
{
public static void Slice1<T>(this IEnumerable<IRange<T>> ranges)
where T : IComparable<T>
{
}
public static void Slice2<R, T>(this IEnumerable<R> ranges)
where R : IRange<T>
where T : IComparable<T>
{
}
}
class Program
{
static void Main(string[] args)
{
IEnumerable<IRange<Int32>> a = new IRange<Int32>[0];
a.Slice1();
IEnumerable<IRange<Int32, String>> b = new IRange<Int32, String>[0];
b.Slice1(); // doesn't compile, and Slice2 doesn't handle either
}
}
}