Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is Activator.CreateInstance<T>() allowed without the new() generic type constraint?

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?

like image 457
Hydrargyrum Avatar asked Sep 12 '16 05:09

Hydrargyrum


2 Answers

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

like image 137
Tamir Vered Avatar answered Sep 29 '22 06:09

Tamir Vered


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.

like image 30
eocron Avatar answered Sep 29 '22 05:09

eocron