I\'d like to know why the new constraint on a generic type parameter can only be applied without parameters, that is, one may constraint the type to have the parameterless c
Kirk Woll's quote from me of course is all the justification that is required; we are not required to provide a justification for features not existing. Features have enormous costs.
However, in this specific case I can certainly give you some reasons why I would push back on the feature if it came up in a design meeting as a possible feature for a future version of the language.
To start with: consider the more general feature. Constructors are methods. If you expect there to be a way to say "the type argument must have a constructor that takes an int" then why is it not also reasonable to say "the type argument must have a public method named Q that takes two integers and returns a string?"
string M<T>(T t) where T has string Q(int, int)
{
return t.Q(123, 456);
}
Does that strike you as a very generic thing to do? It seems counter to the idea of generics to have this sort of constraint.
If the feature is a bad idea for methods, then why is it a good idea for methods that happen to be constructors?
Conversely, if it is a good idea for methods and constructors, then why stop there?
string M<T>(T t) where T has a field named x of type string
{
return t.x;
}
I say that we should either do the whole feature or don't do it at all. If it is important to be able to restrict types to have particular constructors, then let's do the whole feature and restrict types on the basis of members in general and not just constructors.
That feature is of course a lot more expensive to design, implement, test, document and maintain.
Second point: suppose we decided to implement the feature, either the "just constructors" version or the "any member" version. What code do we generate? The thing about generic codegen is that it has been carefully designed so that you can do the static analysis once and be done with it. But there is no standard way to describe "call the constructor that takes an int" in IL. We would have to either add a new concept to IL, or generate the code so that the generic constructor call used Reflection.
The former is expensive; changing a fundamental concept in IL is very costly. The latter is (1) slow, (2) boxes the parameter, and (3) is code that you could have written yourself. If you're going to use reflection to find a constructor and call it, then write the code that uses reflection to find a constructor and call it. If this is the code gen strategy then the only benefit that the constraint confers is that the bug of passing a type argument that does not have a public ctor that takes an int is caught at compile time instead of runtime. You don't get any of the other benefits of generics, like avoiding reflection and boxing penalties.
Summary
This is an attempt to capture the current information and workarounds on this question and present it as an answer.
I find Generics combined with Constraints one of the most powerful and elegant aspects of C# (coming from a C++ templates background). where T : Foo
is great as it introduces capability to T while still constraining it to Foo at compile time. In many cases, it has made my implementation simpler. At first, I was a bit concerned as using generic types in this way can cause generics to grow through the code, but I have allowed it to do so and the benefits have greatly outweighed any downsides. However, the abstraction always falls down when it comes to constructing a Generic type that takes a parameter.
The Problem
When constraining a generic class, you are able to indicate that the generic must have a parameterless constructor and then instantiate it:
public class Foo<T>
where T : new()
{
public void SomeOperation()
{
T something = new T();
...
}
}
The problem is that one is only ably to constrain for parameterless constructors. This means that one of the workarounds suggested below needs to be used for constructors that have parameters. As described below, the workarounds have drawbacks ranging from requiring additional code to being very dangerous. Also, if I have a class that has a public parameterless constructor that is used by a generic method, but somewhere down the track that class is changed so that the constructor now has a parameter then I need to change the design of the template and surrounding code to use one of the workarounds rather than new().
This is something that Microsoft definitely knows about, see these links on Microsoft Connect just as a sample (not counting the confused Stack Overflow users asking the question) here here here here here here here.
They are all closed as 'Won't Fix' or 'By Design'. The sad thing about that is that the issue is then locked and it is no longer possible to vote them up. However you can vote here for the constructor feature.
The Workarounds
There are three main types of workarounds, none of which are ideal: -
The Explanations
A standard response is provided on Microsoft Connect which is:
"Thank you for your suggestion. Microsoft has received a number of suggestions on changes to the constraint semantics of generic types, as well as doing its own work in this area. However at this time Microsoft cannot give any undertaking that changes in this area will be part of a future product release. Your suggestion will be noted to help drive decisions in this area. In the meantime the code sample below..."
The workaround is actually not my recommended workaround of all the options as it is not type safe and results in a runtime exception if you ever happen to add another parameter to the constructor.
The best explanation I can find is offered by Eric Lippert in this very stack overflow post. I am very appreciative of this answer, but I think that further discussion is required on this at a user level and then at the technical level (probably by people who know more than me about the internals).
I also recently spotted that there is a good and detail by Mads Torgersen in this link (see "Posted by Microsoft on 3/31/2009 at 3:29 PM").
The problem is that constructors are different from other methods in that we can already constrain methods as much as we need to by way of derivation constraints (interface or base class). There may be some cases where method constraints are beneficial, I have never needed them, however I continuously hit the parameterless constructor limitation. Of course, a general (not constructor-only) solution would be ideal and Microsoft would need to decide on this themselves.
The Suggestions
Regarding the debatable benefit vs difficulty of implementation, I can appreciate this, but would make the following points: -
The Status
Not about to happen in any shape of form as far as I can tell.
If one wants to have a method with a generic type T
whose instances can be created using a single int
parameter, one should have the method accept, in addition to type T
, either a Func<int, T>
or else a suitably-defined interface, possibly using something like:
static class IFactoryProducing<ResultType>
{
interface WithParam<PT1>
{
ResultType Create(PT1 p1);
}
interface WithParam<PT1,PT2>
{
ResultType Create(PT1 p1, PT2 p2);
}
}
(the code would seem nicer if the outer static class could be declared as an interface, but IFactoryProducing<T>.WithParam<int>
seems clearer than IFactory<int,T>
(since the latter is ambiguous as to which type is the parameter and which is the result).
In any case, whenever one passes aroud type T
one also passes around a suitable factory delegate or interface, one can achieve 99% of what one could achieve with parameterized constructor constraints. The run-time cost can be minimized by having each constructable type generate a static instance of a factory, so it won't be necessary to create factory instances in any sort of looping context.
BTW, beyond the cost of the feature, there would almost certainly be some substantial limitations which would make it less versatile than the workaround. If constructor constraints are not contravariant with regard to parameter types, it may be necessary to pass around a type parameter for the exact type required for the constructor constraint, in addition to the actual type of the parameter to be used; by the time one does that, one might as well pass around a factory. If they are contravariant, then one runs into trouble resolving which constructor should be called if a generic type has constraint new(Cat, ToyotaTercel)
, and the actual type just has constructors new(Animal, ToyotaTercel)
and new(Cat, Automobile)
.
PS--To clarify the problem, contravariant constructor constraints lead to a variation of the "double diamond" problem. Consider:
T CreateUsingAnimalAutomobile<T>() where T:IThing,new(Animal,Automobile)
{ ... }
T CreateUsingAnimalToyotaTercel<T>() where T:IThing,new(Animal,ToyotaTercel)
{ return CreateUsingAnimalAutomobile<T>(); }
T CreateUsingCatAutomobile<T>() where T:IThing,new(Cat,Automobile)
{ return CreateUsingAnimalAutomobile<T>(); }
IThing thing1=CreateUsingAnimalToyotaTercel<FunnyClass>(); // FunnyClass defined in question
IThing thing2=CreateUsingCatAutomobile<FunnyClass>(); // FunnyClass defined in question
In processing the call to CreateUsingAnimalToyotaTercel<FunnyClass>()
, the "Animal,ToyotaTercel" constructor should satisfy the constraint for that method, and the generic type for that method should satisfy a constraint for CreateUsingAnimalAutomobile<T>()
. In processing the call to CreateUsingCatAutomobile<FunnyClass>()
, the "Cat,Automobile" constructor should satisfy the constraint for that method, and the generic type for that method should satisfy the constraint for CreateUsingAnimalAutomobile<T>()
.
The problem is that both calls will invoke a call to the same CreateUsingAnimalAutomobile<SillyClass>()
method, and that method has no way of knowing which constructor should be invoked. Contravariance-related ambiguities aren't unique to constructors, but in most cases they're resolved through compile-time binding.