What are the key uses of a Static
Generic Class in C#? When should they
be used? What examples best illustrate
their usage?
I think in general, you should avoid creating type parameters on static classes, otherwise you can't depend on type-inference to make your client code more concise.
To give a concrete example, let's say you're writing a static utility class to handle operations on Lists. You could write the class two ways:
// static class with generics
public static class ListOps<T>
{
public static List<T> Filter(List<T> list, Func<T, bool> predicate) { ... }
public static List<U> Map<U>(List<T> list, Func<T, U> convertor) { ... }
public static U Fold<U>(List<T> list, U seed, Func<T, U> aggregator) { ... }
}
// vanilla static class
public static class ListOps
{
public static List<T> Filter<T>(List<T> list, Func<T, bool> predicate) { ... }
public static List<U> Map<T, U>(List<T> list, Func<T, U> convertor) { ... }
public static U Fold<T, U>(List<T> list, U seed, Func<T, U> aggregator) { ... }
}
But classes are equivalent, but which one is easier to use? Compare:
// generic static class
List<int> numbers = Enumerable.Range(0, 100).ToList();
List<int> evens = ListOps<int>.Filter(numbers, x => x % 2 = 0);
List<string> numberString = ListOps<int>.Map(numbers, x => x.ToString());
int sumOfSquares = ListOps<int>.Fold(numbers, 0, (acc, x) => acc + x*x);
// vanilla static class
List<int> numbers = Enumerable.Range(0, 100).ToList();
List<int> evens = ListOps.Filter(numbers, x => x % 2 = 0);
List<string> numberString = ListOps.Map(numbers, x => x.ToString());
int sumOfSquares = ListOps.Fold(numbers, 0, (acc, x) => acc + b * b);
In my opinion, ListOps<someType>
is bulky and clumsy. The definition of the vanilla class is slightly larger, but client code is easier to read -- thanks to type inference.
In the worst case, C# can't infer the types, and you have to specify them by hand. Would you rather write ListOps<A>.Map<B>(...)
or ListOps.Map<A, B>(...)
? My preference is toward the latter.
The strategy above works particularly well when your static class holds no mutable state, or its mutable state is known at compile time.
If the static class holds mutable state whose type isn't determined at compile time, then you probably have a use case for a static class with generic params. Hopefully these occasions are few and far between, but when it happens, you'll be happy C# supports the functionality.