Страницы

Поиск по вопросам

пятница, 29 ноября 2019 г.

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

#c_sharp #net #рефлексия


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

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


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

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

 public class ObjectCreator where T: new()
{
    protected Func V4Lambda;

    protected Func V5Lambda;

    public ObjectCreator()
    {
        Type sType = typeof(T);

        //V4
        V4Lambda = Expression.Lambda>(Expression.New(sType)).Compile();

        //V5
        V5Lambda = DynamicModuleLambdaCompiler.GenerateFactory();
    }

    public T V1()
    {
        return (T)Activator.CreateInstance(typeof(T));
    }

    public T V2()
    {
        return new T();
    }

    public T V3()
    {
        return CustomActivator.CreateInstance();
    }

    public T V4()
    {
        return V4Lambda();
    }

    public T V5()
    {
        return V5Lambda();
    }
}

public static class CustomActivator
{
    public static T CreateInstance() where T : new()
    {
        return ActivatorImpl.Factory();
    }

    private class ActivatorImpl where T : new()
    {
        private static readonly Expression> _expr = () => new T();

        public static readonly Func Factory = _expr.Compile();
    }
}

public static class DynamicModuleLambdaCompiler
{
    public static Func GenerateFactory() where T : new()
    {
        Expression> 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)method.CreateDelegate(typeof(Func));
    }
}


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


    


Ответы

Ответ 1



В следующих статьях Сергея Теплякова Исследуем new() ограничение в C# Dissecting the new() constraint in C#: a perfect example of a leaky abstraction делается вывод, что самый быстрый способ создания объекта - это распарсить лямбду в выражение и скомпилировать его: public static class DynamicModuleLambdaCompiler { public static Func GenerateFactory() where T:new() { Expression> 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)method.CreateDelegate(typeof(Func)); } } public static class FastActivator where T : new() { /// /// Extremely fast generic factory method that returns an instance /// of the type . /// public static readonly Func Create = DynamicModuleLambdaCompiler.GenerateFactory(); } 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 |

Ответ 2



Вы не должны заниматься низкоуровневыми микрооптимизациями практически никогда. (Потому что компилятор рано или поздно сумеет оптимизировать лучше.) Но если в каком-то месте вам реально нужна оптимизация, то достаточно просто передать создающую функцию Func куда нужно, и всё: public class ObjectCreator<Т> { Func create; public ObjectCreator(Func 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 |

Комментариев нет:

Отправить комментарий