Uses for static generic classes?

33,334

Solution 1

I use static generic classes for caching reflection-heavy code.

Let's say I need to build an expression tree that instantiates objects. I build it once in the static constructor of the class, compile it to a lambda expression, then cache it in a member of the static class. I often don't make these classes publicly assessable - they are usually helpers for other classes. By caching my expressions in this way, I avoid the need to cache my expressions in some sort of Dictionary<Type, delegate>.

There is an example of this pattern in the BCL. The (DataRow) extension methods Field<T>() and SetField<T>() use the (private) static generic class System.Data.DataRowExtensions+UnboxT<T>. Check it out with Reflector.

Solution 2

Making a class static doesn't add any functionality - it's just a convenient check if you intend to use a class without instantiating it. And there are several uses for that...

You can use static generic classes to work around a limitation: C# doesn't permit partial specialization. That means you must either specify all type parameters or none. However, that can be needlessly verbose.

For example:

static class Converter {
    public TOut Convert<TIn, TOut>(TIn x) {...}
}

the previous class doesn't permit type inference since inference doesn't work on return values. However, you also can't specify the return value type without also specifying the input type since you can't partially specialize. Using a (possibly static) generic class, you can specify only one of the two types:

static class ConvertTo<TOut> {
    public TOut Convert<TIn>(TIn x) {...}
}

That way you can let type inference work on the parameter type and specify only the return type.

(Although the above case is conceivable, it doesn't require the generic class to be static, of course).


Secondly, (as Steven first pointed out) a separate static fields exists for each constructed type, and that makes static classes great places to store extra information about types or type combinations. In essence, it's a semi-static hashtable that keys on types.

A semi-static lookup table keying on types sounds a little arcance, but it's actually a very, very useful structure because it allows you to store expensive reflection and code-generation results where they're almost free to look up (cheaper than a dictionary because it gets JIT-ed in and you avoid a call to .GetType()). If you're doing metaprogramming, this is great!

For example, I use this in ValueUtils to store generated hash functions:

//Hash any object:
FieldwiseHasher.Hash(myCustomStructOrClass);

//implementation:
public static class FieldwiseHasher {
    public static int Hash<T>(T val) { return FieldwiseHasher<T>.Instance(val); }
}

public static class FieldwiseHasher<T> {
    public static readonly Func<T, int> Instance = CreateLambda().Compile();
    //...
}

Static generic methods allow type-inference to make usage really easy; static fields on generic classes allow virtually overhead-free storage of (meta)data. It wouldn't surprise me at all if ORM's like Dapper and PetaPoco use techniques like this; but it's also great for (de)serializers. A limitation is that you're getting the low overhead because you're binding to the compile-time type; if the object that's passed is actually an instance of a subclass, you're probably binding to the wrong type - and adding checks to avoid that kind of undermines the benefit of being low-overhead.

Solution 3

Static fields of a generic type are specific to the actual type T. This means you can store a type specific cache internally. This could be a reason to create a static generic type. Here is a (rather useless, but informative) example:

public static TypeFactory<T> where T : new()
{
    // There is one slot per T.
    private static readonly object instance = new T();

    public static object GetInstance() {
        return instance;
    }
}

string a = (string)TypeFactory<string>.GetInstance();
int b = (int)TypeFactory<int>.GetInstance();

Solution 4

One use of static generic classes that I recently learned was possible is to define a constraint on the class level for the type, then the constraint applies to all the static members of the class, what I also learned is that this is not allowed for static generic classes that define extention methods.

For example if you are going to create a static generic class and you know all the generic types should be IComparable (just an example) then you can do the following.

static class MyClass<T> where T : IComparable
{
  public static void DoSomething(T a, T b)
  {
  }

  public static void DoSomethingElse(T a, T b)
  {
  }
}

Notice that I did not need to apply the constraint to all the member, but just at the class level.

Solution 5

The first blog post you mention shows a valid use (as a static repository class for an ActiveRecord implementation):

public static class Repository<T>
{
    public static T[] FindAll { }

    public static T GetById(int id){ }

    public static void Save(T item) { }
}

Or if you wanted to implement different sort methods for Generic arrays:

public static class ArraySort<T>
{
    public static T[] BubbleSort(T[] source) { }

    public static T[] QuickSort(T[] source) { }
}
Share:
33,334
Ian Mercer
Author by

Ian Mercer

I own the World's Smartest House - a .NET powered house that saves &gt; 40% of the energy it would otherwise use, a house that you can chat to using a Natural Language Interface, a house that knows where you are and can play music, adjust lights, heating, turn on sprinklers, ... See the demo and read much more on my blog at blog. Follow me on twitter too, see link on blog

Updated on December 08, 2020

Comments

  • Ian Mercer
    Ian Mercer over 3 years

    What are the key uses of a Static Generic Class in C#? When should they be used? What examples best illustrate their usage?

    e.g.

    public static class Example<T>
    {
       public static ...
    }
    

    Since you can't define extension methods in them they appear to be somewhat limited in their utility. Web references on the topic are scarce so clearly there aren't a lot of people using them. Here's a couple:-

    http://ayende.com/Blog/archive/2005/10/05/StaticGenericClass.aspx

    Static Generic Class as Dictionary


    Summary of Answers Given

    The key issues appear to be "What's the difference between a static generic class with static methods and a non-generic static class with static generic members?"

    The decision as to which to use appears to revolve around "Does the class need to store type-specific state internally?"

    If there is no need for type-specific internal storage then a static non-generic class with generic static methods appears to be preferable because the calling syntax is nicer and you can define extension methods within it.

  • Ian Mercer
    Ian Mercer about 14 years
    I'm looking more for general guidance on when they are beneficial. How is your #2 better than a non-generic static class with generic methods in it like:- public static T[] BubbleSort<T>(T[] source)?
  • Justin Niessner
    Justin Niessner about 14 years
    @Hightechrider the staic class would allow you to have shared private static resources that match the type of T if you needed. Otherwise, it just saves you some keystrokes.
  • Ian Mercer
    Ian Mercer about 14 years
    Thanks, +1. So far we have: Use them (A) If it has state per-derived-type, or (B) if you want constraints on the generic Type(s) and want less to type. Is that it?
  • Ian Mercer
    Ian Mercer about 14 years
    But generic methods in a static class give almost the same benefit (no cut and paste needed there to work with any T), the difference appears to be that A) the generic static class can have state that is 'per-derived-type' as opposed to only a single state for all derived classes and B) it's less typing sometimes to define T and its constraints once at the top of the class. Is that it?
  • Steven Sudit
    Steven Sudit about 14 years
    Well, you can actually add constraints on a per-method basis when the class itself is not generic, so that's not the issue, either. Other than that, I think what you have is basically right.
  • Ian Mercer
    Ian Mercer about 14 years
    Thanks. I think the argument that the latter can also be turned into extension methods is particularly relevant for your example! One point I don't get is the last section regarding 'type not determined at compile time' - that sounds like a use for dynamic not generics which are determined at compile time. Doesn't the issue boil down to "Is a separate mutable state required for each subclass of the generic static class or is there just one mutable state stored for all derivatives?"?
  • John Saunders
    John Saunders about 14 years
    You've pretty much got it right, but you're underestimating the convenience. I've just gone through a major refactoring effort that required heavy use of generic classes, methods, interfaces and delegates. There were some situations where you had to use the same type parameter, to "pass the type" from where it's instantiated all the way through. Otherwise, types don't match: type T in one method might not be the same as type T in another.
  • Ian Mercer
    Ian Mercer about 14 years
    But were any of them generic static classes? Clearly generic non-static classes are awesomely useful because you do share typed information between methods, but in a generic static class the only state you can share is static state and that seems to mean that there are only very limited scenarios where they would ever be preferable to either a generic non-static class (instantiated as a singleton if necessary) or a non-generic static class with generic static methods.
  • John Saunders
    John Saunders about 14 years
    I'd have to check. Several of these classes moved back and forth between being static and being instance classes. My point was only that the same issues I had with multiple generic classes interacting would also apply to static classes.
  • Jim
    Jim almost 8 years
    it would be informative to have a sample of the static lambda expression creating constructor as part of the answer.
  • M.kazem Akhgary
    M.kazem Akhgary over 7 years
    Very nice. using static generic factory took out the need for casting to T.
  • ChaosPandion
    ChaosPandion about 5 years
    How come you made the changes you did in '16? Why not make instance of type T?
  • Steven
    Steven almost 3 years
    @ChaosPandion: That's a long time ago, I'm not sure, but I think I wanted to simplify the example and also demonstrate that even if when the static member was of type Object, each closed generic type would still have its own instance.