If I have a type parameter constraint new()
:
void Foo<T>() where T : new()
{
var t = new T();
}
Is it true that new T()
will internally use the Activator.CreateInstance
method (i.e. reflection)?
The Activator. CreateInstance method creates an instance of a type defined in an assembly by invoking the constructor that best matches the specified arguments. If no arguments are specified then the constructor that takes no parameters, that is, the default constructor, is invoked.
CreateInstance(ActivationContext, String[]) Creates an instance of the type that is designated by the specified ActivationContext object and activated with the specified custom activation data. CreateInstance(Type) Creates an instance of the specified type using that type's parameterless constructor.
The new constraint specifies that a type argument in a generic class or method declaration must have a public parameterless constructor. To use the new constraint, the type cannot be abstract.
Activator Class in . NET 4.0. Contains methods to create types of objects locally or remotely, or obtain references to existing remote objects. ActivatorClassSample.rar. Contains methods to create types of objects locally or remotely, or obtain references to existing remote objects.
Yes, this is true. Edit 2: Here's a good explanation of the how and why.
http://www.simple-talk.com/community/blogs/simonc/archive/2010/11/17/95700.aspx
For verification I compiled the following method:
public static T Create<T>() where T: new() {
return new T();
}
And this is the generated IL when compiled with the C# compiler in .NET 3.5 SP1:
.method public hidebysig static !!T Create<.ctor T>() cil managed
{
.maxstack 2
.locals init (
[0] !!T local,
[1] !!T local2)
L_0000: ldloca.s local
L_0002: initobj !!T
L_0008: ldloc.0
L_0009: box !!T
L_000e: brfalse.s L_001a
L_0010: ldloca.s local2
L_0012: initobj !!T
L_0018: ldloc.1
L_0019: ret
L_001a: call !!0 [mscorlib]System.Activator::CreateInstance<!!T>()
L_001f: ret
}
Edit: The C# 4 compiler creates slightly different, but similar, code:
.method public hidebysig static !!T Create<.ctor T>() cil managed
{
.maxstack 2
.locals init (
[0] !!T CS$1$0000,
[1] !!T CS$0$0001)
L_0000: nop
L_0001: ldloca.s CS$0$0001
L_0003: initobj !!T
L_0009: ldloc.1
L_000a: box !!T
L_000f: brfalse.s L_001c
L_0011: ldloca.s CS$0$0001
L_0013: initobj !!T
L_0019: ldloc.1
L_001a: br.s L_0021
L_001c: call !!0 [mscorlib]System.Activator::CreateInstance<!!T>()
L_0021: stloc.0
L_0022: br.s L_0024
L_0024: ldloc.0
L_0025: ret
}
In the case of a value type it doesn't use the activator but just returns the default(T)
value, otherwise it invokes the Activator.CreateInstance
method.
Yes. It does for reference types.
Using ILSpy on the following release-compiled code:
public static void DoWork<T>() where T: new()
{
T t = new T();
Console.WriteLine(t.ToString());
}
Yielded
.method public hidebysig
instance void DoWork<.ctor T> () cil managed
{
// Method begins at RVA 0x2064
// Code size 52 (0x34)
.maxstack 2
.locals init (
[0] !!T t,
[1] !!T CS$0$0000,
[2] !!T CS$0$0001
)
IL_0000: ldloca.s CS$0$0000
IL_0002: initobj !!T
IL_0008: ldloc.1
IL_0009: box !!T
IL_000e: brfalse.s IL_001b
IL_0010: ldloca.s CS$0$0001
IL_0012: initobj !!T
IL_0018: ldloc.2
IL_0019: br.s IL_0020
IL_001b: call !!0 [mscorlib]System.Activator::CreateInstance<!!T>()
IL_0020: stloc.0
IL_0021: ldloca.s t
IL_0023: constrained. !!T
IL_0029: callvirt instance string [mscorlib]System.Object::ToString()
IL_002e: call void [mscorlib]System.Console::WriteLine(string)
IL_0033: ret
} // end of method Program::DoWork
Or in C#:
public void DoWork<T>() where T : new()
{
T t = (default(T) == null) ? Activator.CreateInstance<T>() : default(T);
Console.WriteLine(t.ToString());
}
JIT will create different compiled instructions for each different value type parameter passed in, but will use the same instructions for reference types -- hence the Activator.CreateInstance()
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