Reputation: 3816
In the sample code shown below, the "CompileError" method won't compile, because it requires the where T : new()
constraint as shown in the CreateWithNew()
method. However, the CreateWithActivator<T>()
method compiles just fine without a constraint.
public class GenericTests
{
public T CompileError<T>() // compile error CS0304
{
return new T();
}
public T CreateWithNew<T>() where T : new() // builds ok
{
return new T();
}
public T CreateWithActivator<T>() // builds ok
{
return Activator.CreateInstance<T>();
}
}
Why is this?
According to https://stackoverflow.com/a/1649108/531971, which references MSDN documentation, and this question, the new T()
expression in generics is actually implemented using Activator.CreateInstance<T>()
. So I don't understand why calling new T()
requires that the generic type be constrained in a way that can be omitted when using Activator.CreateInstance<T>()
.
Or, to put the question the other way around: what's the point of the where T : new()
constraint, if it's easy to create instances of T
in a generic method without the constraint, by directly using the exact same underlying infrastructure?
Upvotes: 11
Views: 2408
Reputation: 10287
There is a conceptual difference between Activator
and T()
:
Activator.CreateInstance<T>
— I want to create a new instance of T
using its default constructor — And throw an Exception
if it doesn't have one (Since something very wrong has happened and I want to handle it/throw it myself).
Side note: keep in mind that as MSDN says:
In general, there is no use for the
CreateInstance<T>()
generic method in application code, because the type must be known at compile time. If the type is known at compile time, normal instantiation syntax can be used.
since generally you would want to use a constructor when the Type
is known at compile time (CreateInstance<T>()
uses RuntimeTypeHandle.CreateInstance
which is slower [Also that's why Activator.CreateInstance<T>
itself doesn't need the constraint]).
T()
— I Want to call the empty constructor of T
, supposedly like a standard constructor call.
You don't want a "standard" constructor call to fail because "No such constructor was found", therefore, the compiler wants you to constraint that there is one.
More than that; You should prefer Compile time errors over Exceptions
where possible.
The fact that T()
is implemented internally using reflection irrelevant for the average case of "I just want a default instance of T
" (of course that the internal implementation is important if you care about performance/etc...).
Upvotes: 13
Reputation: 81159
The Activator.CreateInstance<T>()
method is exposed to user code to allow for the possibility that a generic class might be usable in different ways, some of which would require that the type parameter satisfy certain constraints and some of which don't. For example, a class Foo<T>
might support any of the following usage patterns:
Client code supplies a function that returns a new T
.
Client code defers to a default function which creates a new T
using its default constructor.
Client code avoids using any features of the class that would require it to create new instances of T
.
Patterns #1 and #3 should be usable with any T
, while #2 should only work with types having parameterless constructors. Having Activator.CreateInstance<T>()
compile for an unconstrained T
, and work for types T
that happen to have parameterless constructors, makes it easy to have code support all three usage patterns. If Activator.CreateInstance<T>
had a new
constraint, it would be very awkward to use it with generic type parameters that don't have one.
Upvotes: 1
Reputation: 7526
This is just sugar. Self-control sugar if you can say so. For example, you can call through reflection almost any method in any type (in some cases even without instance!), but this is just not right, don't you agree? Your code will just become unmaintainable at some point, and many errors will pop-up in execution time, this is very bad. So, if you can control yourself before execution - just do it.
Constraint will help you to understand it in compile-time.
Upvotes: 2