Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the c# compiler emit Activator.CreateInstance when calling new in with a generic type with a new() constraint?

When you have code like the following:

static T GenericConstruct<T>() where T : new()
{
    return new T();
}

The C# compiler insists on emitting a call to Activator.CreateInstance, which is considerably slower than a native constructor.

I have the following workaround:

public static class ParameterlessConstructor<T>
    where T : new()
{
    public static T Create()
    {
        return _func();
    }

    private static Func<T> CreateFunc()
    {
        return Expression.Lambda<Func<T>>( Expression.New( typeof( T ) ) ).Compile();
    }

    private static Func<T> _func = CreateFunc();
}

// Example:
// Foo foo = ParameterlessConstructor<Foo>.Create();

But it doesn't make sense to me why this workaround should be necessary.

like image 588
user46267 Avatar asked Dec 15 '08 05:12

user46267


People also ask

Why does the letter c exist?

The letter c was applied by French orthographists in the 12th century to represent the sound ts in English, and this sound developed into the simpler sibilant s.

Does the letter c exist?

C, or c, is the third letter in the English and ISO basic Latin alphabets. Its name in English is cee (pronounced /ˈsiː/), plural cees.

Why does the c make the s sound?

The rule. Here's the rule: When 'c' comes directly before the letters 'e', 'i' or 'y' we use the /s/ sound. in other cases we use a /k/ sound.

Why does c make 2 sounds?

In the Latin-based orthographies of many European languages, including English, a distinction between hard and soft ⟨c⟩ occurs in which ⟨c⟩ represents two distinct phonemes. The sound of a hard ⟨c⟩ often precedes the non-front vowels ⟨a⟩, ⟨o⟩ and ⟨u⟩, and is that of the voiceless velar stop, /k/ (as in car).


2 Answers

I suspect it's a JITting problem. Currently, the JIT reuses the same generated code for all reference type arguments - so a List<string>'s vtable points to the same machine code as that of List<Stream>. That wouldn't work if each new T() call had to be resolved in the JITted code.

Just a guess, but it makes a certain amount of sense.

One interesting little point: in neither case does the parameterless constructor of a value type get called, if there is one (which is vanishingly rare). See my recent blog post for details. I don't know whether there's any way of forcing it in expression trees.

like image 171
Jon Skeet Avatar answered Sep 29 '22 23:09

Jon Skeet


This is likely because it is not clear whether T is a value type or reference type. The creation of these two types in a non-generic scenario produce very different IL. In the face of this ambiguity, C# is forced to use a universal method of type creation. Activator.CreateInstance fits the bill.

Quick experimentation appears to support this idea. If you type in the following code and examine the IL, it will use initobj instead of CreateInstance because there is no ambiguity on the type.

static void Create<T>()
    where T : struct
{
    var x = new T();
    Console.WriteLine(x.ToString());
}

Switching it to a class and new() constraint though still forces an Activator.CreateInstance.

like image 32
JaredPar Avatar answered Sep 29 '22 22:09

JaredPar