Способы создания объектов в C#

353
09 сентября 2017, 23:19

Я знаю 1 способ создать объекта в C#:

public static class ObjectCreator
{
    public static T GetObject<T>() where T : class
    {
        return (T)Activator.CreateInstance(typeof(T));
    }
}

Есть ли более производительные?

Update класс с известными способами:

 public class ObjectCreator<T> where T: new()
{
    protected Func<T> V4Lambda;
    protected Func<T> V5Lambda;
    public ObjectCreator()
    {
        Type sType = typeof(T);
        //V4
        V4Lambda = Expression.Lambda<Func<T>>(Expression.New(sType)).Compile();
        //V5
        V5Lambda = DynamicModuleLambdaCompiler.GenerateFactory<T>();
    }
    public T V1()
    {
        return (T)Activator.CreateInstance(typeof(T));
    }
    public T V2()
    {
        return new T();
    }
    public T V3()
    {
        return CustomActivator.CreateInstance<T>();
    }
    public T V4()
    {
        return V4Lambda();
    }
    public T V5()
    {
        return V5Lambda();
    }
}
public static class CustomActivator
{
    public static T CreateInstance<T>() where T : new()
    {
        return ActivatorImpl<T>.Factory();
    }
    private class ActivatorImpl<T> where T : new()
    {
        private static readonly Expression<Func<T>> _expr = () => new T();
        public static readonly Func<T> Factory = _expr.Compile();
    }
}
public static class DynamicModuleLambdaCompiler
{
    public static Func<T> GenerateFactory<T>() where T : new()
    {
        Expression<Func<T>> expr = () => new T();
        NewExpression newExpr = (NewExpression)expr.Body;
        var method = new DynamicMethod(
            name: "lambda",
            returnType: newExpr.Type,
            parameterTypes: new Type[0],
            m: typeof(DynamicModuleLambdaCompiler).Module,
            skipVisibility: true);
        ILGenerator ilGen = method.GetILGenerator();
        // Constructor for value types could be null
        if (newExpr.Constructor != null)
        {
            ilGen.Emit(OpCodes.Newobj, newExpr.Constructor);
        }
        else
        {
            LocalBuilder temp = ilGen.DeclareLocal(newExpr.Type);
            ilGen.Emit(OpCodes.Ldloca, temp);
            ilGen.Emit(OpCodes.Initobj, newExpr.Type);
            ilGen.Emit(OpCodes.Ldloc, temp);
        }
        ilGen.Emit(OpCodes.Ret);
        return (Func<T>)method.CreateDelegate(typeof(Func<T>));
    }
}

Результаты моего тестирования:

Answer 1

В следующих статьях Сергея Теплякова

  • Исследуем new() ограничение в C#
  • Dissecting the new() constraint in C#: a perfect example of a leaky abstraction

делается вывод, что самый быстрый способ создания объекта - это распарсить лямбду в выражение и скомпилировать его:

public static class DynamicModuleLambdaCompiler
{
    public static Func<T> GenerateFactory<T>() where T:new()
    {
        Expression<Func<T>> expr = () => new T();
        NewExpression newExpr = (NewExpression)expr.Body;
        var method = new DynamicMethod(
            name: "lambda", 
            returnType: newExpr.Type,
            parameterTypes: new Type[0],
            m: typeof(DynamicModuleLambdaCompiler).Module,
            skipVisibility: true);
        ILGenerator ilGen = method.GetILGenerator();
        // Constructor for value types could be null
        if (newExpr.Constructor != null)
        {
            ilGen.Emit(OpCodes.Newobj, newExpr.Constructor);
        }
        else
        {
            LocalBuilder temp = ilGen.DeclareLocal(newExpr.Type);
            ilGen.Emit(OpCodes.Ldloca, temp);
            ilGen.Emit(OpCodes.Initobj, newExpr.Type);
            ilGen.Emit(OpCodes.Ldloc, temp);
        }
        ilGen.Emit(OpCodes.Ret);
        return (Func<T>)method.CreateDelegate(typeof(Func<T>));
    }
}
public static class FastActivator<T> where T : new()
{
    /// <summary>
    /// Extremely fast generic factory method that returns an instance
    /// of the type <typeparam name="T"/>.
    /// </summary>
    public static readonly Func<T> Create =
        DynamicModuleLambdaCompiler.GenerateFactory<T>();
}
                  Method |       Mean |    StdDev |  Gen 0 |
------------------------ |----------- |---------- |------- |
 ActivatorCreateInstance | 95.0161 ns | 1.0861 ns | 0.0005 |
        FuncBasedFactory |  6.5741 ns | 0.0608 ns | 0.0034 |
  FastActivator_T_Create |  5.1715 ns | 0.0466 ns | 0.0034 |
Answer 2

Вы не должны заниматься низкоуровневыми микрооптимизациями практически никогда. (Потому что компилятор рано или поздно сумеет оптимизировать лучше.)

Но если в каком-то месте вам реально нужна оптимизация, то достаточно просто передать создающую функцию Func<T> куда нужно, и всё:

public class ObjectCreator<Т>
{
    Func<T> create;
    public ObjectCreator(Func<T> create) => this.create = create;
    public T GetObject() => create();
}

Вы потеряете немного абстракцию, но вам же ехать, а не шашечки? Это самый быстрый путь.

Ещё быстрее будет просто в нужном месте вызвать нужный конструктор. Потому что вызов делегата небесплатен. С другой стороны, если вам нужны нанооптимизации, вы выбрали себе не ту платформу.

Бенчмарки:

BenchmarkDotNet=v0.10.9, OS=Windows 7 SP1 (6.1.7601)
Processor=Intel Core i7-2600K CPU 3.40GHz (Sandy Bridge), ProcessorCount=8
Frequency=3320429 Hz, Resolution=301.1659 ns, Timer=TSC
  [Host]     : .NET Framework 4.7 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.2053.0
  DefaultJob : .NET Framework 4.7 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.2053.0

                                         Method |      Mean |     Error |    StdDev |
----------------------------------------------- |----------:|----------:|----------:|
                    V1_Activator_CreateInstance | 70.639 ns | 0.3354 ns | 0.3138 ns |
                        V2_NewGenericConstraint | 77.813 ns | 0.2788 ns | 0.2608 ns |
              V3_CustomActivator_CreateInstance | 20.237 ns | 0.0562 ns | 0.0498 ns |
                          V4_Expression_Compile | 11.175 ns | 0.1693 ns | 0.1584 ns |
 V5_DynamicModuleLambdaCompiler_GenerateFactory |  4.822 ns | 0.0322 ns | 0.0301 ns |
                                V6_Factory_Func |  3.967 ns | 0.0247 ns | 0.0231 ns |
                     V7_Direct_Constructor_Call |  3.047 ns | 0.0207 ns | 0.0194 ns |
READ ALSO
После перезахода в сцену кнопка share не работает

После перезахода в сцену кнопка share не работает

Подскажите пожалуйста как решить проблему

235
C#. не Копируется Stream в MemoryStream

C#. не Копируется Stream в MemoryStream

Здравствуйте, есть ответ полученный для httpClientОтвет считываю не в строку, а в поток (т

218
Синхронизация imap (клиент-сервер) c#

Синхронизация imap (клиент-сервер) c#

Добрый день, стоит такая цель : синхронизироваться с почтой и иметь возможность загружать/скачивать письмаЕсть ли такая возможность ? желательно...

263
Создать свой класс [требует правки]

Создать свой класс [требует правки]

Пожалуйста, приведите пример как создать новый тип (класс) age, наследовать его от int, ограничить от 0 до 100Спасибо

324