An Alternative to the C# Generic new() Constraint
Monday, November 12, 2007 – 3:33 PMHere’s something I came up with other the weekend while playing with some code…
I wanted to write a method that ensured that some object of type T was always available.
ModelElement element = EnsureElementExists<ModelElement>(_store);
Unfortunately in this case the ModelElement class does not have a parameterless constructor. This means I can’t use the where T:New() constraint and then just create a new instance in my generic method. What I can do is use a delegate to provide a new instance of the object if I need it:
public delegate T ConstructorDelegate<T>() where T : class; public static T EnsureElementExists<T>(Store store, ConstructorDelegate<T> ctor) where T : ModelElement { T element = store.GetElement<T>(); if (element == null) element = ctor(); return element; }
This means I can now write code like this where DiagramElement (a class derived from ModelElement) is created by the delegate:
DiagramElement diagramElement = EnsureElementExists<DiagramElement>(_store, delegate() { return new DiagramElement(_store); });
Calls to EnsureElementExists can use the anonymous delegate to create a new DiagramElement with a parameterized constructor.
March 2009 Update: Cleaner syntax using lambdas
I was playing around with this again recently and realized that lambda expressions can make the syntax you need to use much simpler and remove the need to define a ConstructorDelegate.
public static T EnsureElementExists<T>(Store store, Func<T> ctor) where T : ModelElement { T element = store.GetElement<T>(); if (element == null) element = ctor(); return element; }
The calling code is further simplified:
DiagramElement diagramElement = EnsureElementExists<DiagramElement>(_store,
() => new DiagramElement(_store));
9 Responses to “An Alternative to the C# Generic new() Constraint”
Nifty idea…the only thing I’m wondering about is why you have a where constraint on ConstructorDelegate. It seems like that would overtly constraint your open-ended solution.
And just to be nitpicky…wouldn’t you add the object you just created in your if block so you wouldn’t keep creating it?
Again, good idea – I like this :)
By Jason Bock on Nov 13, 2007
Nice catch Jason,
I’ve updated the code to reduce the constraint on ConstructorDelegate and fixed the if statement.
Ade
By Ade on Nov 13, 2007
Why not use this?
(T)typeof(T).GetConstructor(
By dreamxpert on May 23, 2009
dreamxpert,
You could use this approach. I prefer the inversion of control pattern where EnsureElementExists knows nothing about how to create an instance of T and relies on the delegate.
I’d also we wary of using GetConstructor() as now you’re using reflection which is likely to be slower than calling a delegate.
Ade
By Ade Miller on May 25, 2009
Thank you for the interesting article.
In the call to EnsureElementExists<T>, the compiler can infer <T> from Func<T> in the argument list, so you can simplify the call from this:
DiagramElement diagramElement = EnsureElementExists<DiagramElement>(_store, () => new DiagramElement(_store));
To this:
DiagramElement diagramElement = EnsureElementExists(_store, () => new DiagramElement(_store));
By JMG on Oct 13, 2010
JMG,
Good catch!
Ade
By Ade Miller on Oct 13, 2010