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?
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...).
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With