A very interesting question, I have never been tempted to use static generic classes, but at least it seems possible.
In the context of declaring extension methods, you cannot only declare extension methods for a certain generic type (such as IEnumerable<T>
) but also bring the type parameter T
into the equation. If we agree to treat IEnumerable<int>
and IEnumerable<string>
as different types, this makes sense on a conceptual level too.
Being able to declare your extension methods in a static generic class would save you repeating your type parameter constraints over and over again, in effect grouping all extension methods for IEnumerable<T> where T : IComparable
together.
According to the specification (citation needed), extension methods can only be declared in static not-nested and not-generic classes. The reason for the first two constraints are fairly obvious:
- May not carry any state as it's not a mixin, just syntactic sugar.
- Must have the same lexical scope as the type it provides extensions for.
The restriction on non-generic static classes seems a little arbitrary to me, I can't come up with a technical reason here. But it might be that the language designers decided to discourage you in writing extension methods that depend on the type parameter of a generic class you wish to provide extension methods for. Instead they want you to provide a truly generic implementation of your extension method but make it possible to provide optimized/specialized (compile-time bound) versions of your extension methods in addition to your general implementation.
Reminds me of template specialization in C++. EDIT: Unfortunately this is wrong, please see my additions below.
Ok, as this is a really interesting topic I did some further research. There actually is a technical restriction that I missed here. Let's look at some code:
public static class Test
{
public static void DoSomething<T>(this IEnumerable<T> source)
{
Console.WriteLine("general");
}
public static void DoSomething<T>(this IEnumerable<T> source) where T :IMyInterface
{
Console.WriteLine("specific");
}
}
This will actually fail with this compiler error:
Type 'ConsoleApplication1.Test'
already defines a member called
'DoSomething' with the same parameter
types
Ok, next we try splitting it up into two different extensions classes:
public interface IMyInterface { }
public class SomeType : IMyInterface {}
public static class TestSpecific
{
public static void DoSomething<T>(this IEnumerable<T> source) where T : IMyInterface
{
Console.WriteLine("specific");
}
}
public static class TestGeneral
{
public static void DoSomething<T>(this IEnumerable<T> source)
{
Console.WriteLine("general");
}
}
class Program
{
static void Main(string[] args)
{
var general = new List<int>();
var specific = new List<SomeType>();
general.DoSomething();
specific.DoSomething();
Console.ReadLine();
}
}
Against my initial impression (it was late yesterday night), this will result in an ambiguity at the call sites. To resolve this ambiguity one would need to call the extension method in a traditional way, but that's against our intention.
So this leaves us with a situation where it's not possible to declare compile-time bound generic specializations of extension methods. On the other hand, there is still no reason why we couldn't declare extension methods only for a single special generic type parameter. Therefore it'd be nice to declare them in a static generic class.
On the other hand writing an extension method like:
public static void DoSomething<T>(this IEnumerable<T> source) where T : IMyInterface {}
or
public static void DoSomething(this IEnumerable<IMyInterface> source) {}
is not all too different and only requires a little casting on the call site vs. on the extension method side (you will probably implement some optimization that depends on the specific type, so you'd need to cast T to IMyInterface or whatever anyway). So the only reason I can come up with is again, the language designers want to encourage you in writing generic extensions only in a truly generic fashion.
Some interesting things could happen here if we take co/contravariance in to the equation, which are about to be introduced with C# 4.0.