views:

360

answers:

10

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

http://stackoverflow.com/questions/686630/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.


@Steven: said "Static fields of a generic type are specific to the actual type T. This means you can store a type specific cache internally".

@Justin Niessner: "To have shared private static resources that match the type of T if you needed"

@Steven Sudit: "In general, if it contains state, not just methods, there may be a point to this."

Others pointed out the benefits or downsides of each relating to how they were used:-

@Chris Taylor: Define a constraint on the class level for the type, then the constraint applies to all the static members of the class.

@Juliet: static classes with generic methods when "your static class holds no mutable state" make your "client code ... easier to read".

Some gave specific advice on which to use and scenarios:

@Juliet: "I think in general, you should avoid creating type parameters on static classes."

@Greg: "I use static generic classes for caching reflection-heavy code." and "I avoid the need to cache my expressions in some sort of Dictionary". He also pointed out: "these classes aren't publicly assessable, they are usually helpers for other classes" which addresses the issue of clumsy syntax pointed out by others.

In the end I marked Greg's answer as accepted because it contained the best example and reasoning, but there were lots of others who added value here.

A: 

It can be

Repository<T>
dario-g
A: 

I have a use for them. Rather than post code I'll just describe. I have a series of classes which can change over time. So I created a DataTable loaded from some source which has the Property Paths in it. (Ex. Object1.Object2.Object3.Value = "The Value"). I use reflection to create the properties which are null and eventually set the final property to the value passed in. All of this is of course done through reflection (I'm not terribly concerned about speed, it is pretty quick as is).

So, I have a couple of ways which I can select data from the initial DataTable in which to create the outer most object (Object1 in the Ex.). Instead of creating a function for each of the classes I create I use a static generic function inside a static class.

Ex.

CustomObjectType = Factory.CreateAnyObjectType(dt, "row select criteria");

The CreateAnyObjectType function would look like this..

internal static T CreateAnyObjectType<T>(DataTable dt, string filter)
{
    T value;
    .. call reflection method to create/set properties
    return T;
}
gbogumil
You're showing the use of a static generic method, not a static generic class.
Justin Niessner
+2  A: 

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) { }
}
Justin Niessner
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)?
Hightechrider
@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.
Justin Niessner
+1  A: 

You're right: they're not much use. Perhaps there are some rare cases that are exceptions, though. For example, what if the class was a type-specific repository, as in Justin's example, but kept a static collection as a cache? In general, if it contains state, not just methods, there may be a point to this.

Steven Sudit
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?
Hightechrider
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.
Steven Sudit
+4  A: 

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.

Chris Taylor
+4  A: 

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>
{
    // There is one slot per T.
    private static ReadOnlyDictionary<string, T> cache;

    public static CreateInstance(string name)
    {
        return cache[name];
    }
}

string a = TypeFactory<string>("foo");
int b = TypeFactory<int>("foo");
Steven
A: 

A static generic class is exactly as useful as any given static class. The difference is that you don't have to use copy-and-paste to create a version of the static class for each type you want it to work on. You make the class generic, and you can "generate" one version for each set of type parameters.

John Saunders
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?
Hightechrider
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.
John Saunders
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.
Hightechrider
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.
John Saunders
+2  A: 

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.

Greg
+1  A: 

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.

Juliet
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?"?
Hightechrider
+1  A: 

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).

Eamon Nerbonne