Hvordan til at passere ctor args i Aktivator.CreateInstance eller brug IL?

Jeg har brug for en udvidet performance Aktivator.CreateInstance() og kom på tværs af i denne artikel af Miron Abramson, der bruger en fabrik til at oprette eksempel i IL og derefter cache det. (Jeg har i prisen-koden nedenfor fra Miron Abramson ‘ s hjemmeside i tilfælde af, at det på en eller anden måde forsvinder). Jeg er ny til IL Udsender kode og noget ud over Aktivator.CreateInstance() for at instantiere en klasse, og enhver hjælp ville være meget taknemmelige.

Mit problem er, at jeg er nødt til at oprette en forekomst af et objekt, der tager en ctor med en parameter. Jeg kan se der er en måde at videregive i den Type af parameter, men er der en måde at passere i værdien af ctor parameter?

Hvis det er muligt, vil jeg gerne bruge en metode, der svarer til CreateObjectFactory<T>(params object[] constructorParams) som nogle objekter, jeg ønsker at instantiere kan have mere end 1 ctor param.


//Source: http://mironabramson.com/blog/post/2008/08/Fast-version-of-the-ActivatorCreateInstance-method-using-IL.aspx
public static class FastObjectFactory
{
    private static readonly Hashtable creatorCache = Hashtable.Synchronized(new Hashtable());
    private readonly static Type coType = typeof(CreateObject);
    public delegate object CreateObject();

    ///
    ///Create an object that will used as a 'factory' to the specified type T 
   ///
    public static CreateObject CreateObjectFactory() where T : class
    {
        Type t = typeof(T);
        FastObjectFactory.CreateObject c = creatorCache[t] as FastObjectFactory.CreateObject;
        if (c == null)
        {
            lock (creatorCache.SyncRoot)
            {
                c = creatorCache[t] as FastObjectFactory.CreateObject;
                if (c != null)
                {
                    return c;
                }
                DynamicMethod dynMethod = new DynamicMethod("DM$OBJ_FACTORY_" + t.Name, typeof(object), null, t);
                ILGenerator ilGen = dynMethod.GetILGenerator();

                ilGen.Emit(OpCodes.Newobj, t.GetConstructor(Type.EmptyTypes));
                ilGen.Emit(OpCodes.Ret);
                c = (CreateObject)dynMethod.CreateDelegate(coType);
                creatorCache.Add(t, c);
            }
        }
        return c;
    }
}

Opdatering til Miron kode fra bruger på sin post 2010-01-11

public static class FastObjectFactory2<T> where T : class, new()
{
    public static Func<T> CreateObject { get; private set; }

    static FastObjectFactory2()
    {
        Type objType = typeof(T);
        var dynMethod = new DynamicMethod("DM$OBJ_FACTORY_" + objType.Name, objType, null, objType);
        ILGenerator ilGen = dynMethod.GetILGenerator();
        ilGen.Emit(OpCodes.Newobj, objType.GetConstructor(Type.EmptyTypes));
        ilGen.Emit(OpCodes.Ret);
        CreateObject = (Func<T>)
        dynMethod.CreateDelegate(typeof(Func<T>));
    }
}
Jeg er at finde (forventet), at antallet af iterationer i høj grad bestemmer, hvilke Aktivator at bruge. For eksempel, en cache ser ud til at virke godt, når der er mindre end 99,999 iterationer. Hvis der er mere end som så ikke en cache er hurtigere.

OriginalForfatteren thames | 2010-01-07

8 svar

  1. 6

    Jeg har gjort lidt af test med dette og som en opfølgning på Miron ‘ s oprindelige artikel (her), har jeg opdaget, at .NET 4,0 Activator er meget hurtigere end før. Nogle resultater fra en version af sin app finjusteret for at vise tider i millisekunder:

    .NET 3.5 build
    
    Number of iterates: 1000000
    Activator.CreateInstance(Type):                           4150
    Activator.CreateInstance<T>():                            1288
    FastObjectFactory.CreateObjec (empty cache):                33
    FastObjectFactory.CreateObjec (cache full):                 28
    ItemFactory.GetNewItem:                                   1283
    
    
    .NET 4.0 build
    
    Number of iterates: 1000000
    Activator.CreateInstance(Type):                            138
    Activator.CreateInstance<T>():                             151
    FastObjectFactory.CreateObjec (empty cache):                28
    FastObjectFactory.CreateObjec (cache full):                 22
    ItemFactory.GetNewItem:                                    156

    Dette var dog for en ikke-parameter konstruktør og jeg har også bemærket, at activator er stadig lidt langsom når konstruktører med parametre, der er anvendt, som kan bse ses nedenfor.

    Et problem jeg havde med den oprindelige løsning offentliggjort her, er, at jeg ikke nødvendigvis kender den type af objekter, vil jeg på samme tid – jeg har kun en Type reference. Nu (medmindre jeg bliver en duffer), som betyder, at jeg ikke kan bruge den generiske løsning her, eller en simpel variation på det.

    Så det er en version jeg har slået sammen, hvilket løser problemet. Det viste også op den lille langsommelighed i den .NET 4,0 Aktivator, når constructor parametre anvendes:

    //For use with no-parameter constructors. Also contains constants and utility methods
    public static class FastActivator
    {
        //THIS VERSION NOT THREAD SAFE YET
        static Dictionary<Type, Func<object>> constructorCache = new Dictionary<Type, Func<object>>();
    
        private const string DynamicMethodPrefix = "DM$_FastActivator_";
    
        public static object CreateInstance(Type objType)
        {
            return GetConstructor(objType)();
        }
    
        public static Func<object> GetConstructor(Type objType)
        {
            Func<object> constructor;
            if (!constructorCache.TryGetValue(objType, out constructor))
            {
                constructor = (Func<object>)FastActivator.BuildConstructorDelegate(objType, typeof(Func<object>), new Type[] { });
                constructorCache.Add(objType, constructor);
            }
            return constructor;
        }
    
        public static object BuildConstructorDelegate(Type objType, Type delegateType, Type[] argTypes)
        {
            var dynMethod = new DynamicMethod(DynamicMethodPrefix + objType.Name + "$" + argTypes.Length.ToString(), objType, argTypes, objType);
            ILGenerator ilGen = dynMethod.GetILGenerator();
            for (int argIdx = 0; argIdx < argTypes.Length; argIdx++)
            {
                ilGen.Emit(OpCodes.Ldarg, argIdx);
            }
            ilGen.Emit(OpCodes.Newobj, objType.GetConstructor(argTypes));
            ilGen.Emit(OpCodes.Ret);
            return dynMethod.CreateDelegate(delegateType);
        }
    }
    
    //For use with one-parameter constructors, argument type = T1
    public static class FastActivator<T1>
    {
        //THIS VERSION NOT THREAD SAFE YET
        static Dictionary<Type, Func<T1, object>> constructorCache = new Dictionary<Type, Func<T1, object>>();
        public static object CreateInstance(Type objType, T1 arg1)
        {
            return GetConstructor(objType, new Type[] { typeof(T1) })(arg1);
        }
        public static Func<T1, object> GetConstructor(Type objType, Type[] argTypes)
        {
            Func<T1, object> constructor;
            if (!constructorCache.TryGetValue(objType, out constructor))
            {
                constructor = (Func<T1, object>)FastActivator.BuildConstructorDelegate(objType, typeof(Func<T1, object>), argTypes);
                constructorCache.Add(objType, constructor);
            }
            return constructor;
        }
    }
    
    //For use with two-parameter constructors, argument types = T1, T2
    public static class FastActivator<T1, T2>
    {
        //THIS VERSION NOT THREAD SAFE YET
        static Dictionary<Type, Func<T1, T2, object>> constructorCache = new Dictionary<Type, Func<T1, T2, object>>();
        public static object CreateInstance(Type objType, T1 arg1, T2 arg2)
        {
            return GetConstructor(objType, new Type[] { typeof(T1), typeof(T2) })(arg1, arg2);
        }
    
        public static Func<T1, T2, object> GetConstructor(Type objType, Type[] argTypes)
        {
            Func<T1, T2, object> constructor;
            if (!constructorCache.TryGetValue(objType, out constructor))
            {
                constructor = (Func<T1, T2, object>)FastActivator.BuildConstructorDelegate(objType, typeof(Func<T1, T2, object>), argTypes);
                constructorCache.Add(objType, constructor);
            }
            return constructor;
        }
    }
    
    //For use with three-parameter constructors, argument types = T1, T2, T3
    //NB: could possibly merge these FastActivator<T1,...> classes and avoid generic type parameters
    //but would need to take care that cache entries were keyed to distinguish constructors having 
    //the same number of parameters but of different types. Keep separate for now.
    public static class FastActivator<T1, T2, T3>
    {
        //THIS VERSION NOT THREAD SAFE YET
        static Dictionary<Type, Func<T1, T2, T3, object>> constructorCache = new Dictionary<Type, Func<T1, T2, T3, object>>();
        public static object CreateInstance(Type objType, T1 arg1, T2 arg2, T3 arg3)
        {
            return GetConstructor(objType, new Type[] { typeof(T1), typeof(T2), typeof(T3) })(arg1, arg2, arg3);
        }
    
        public static Func<T1, T2, T3, object> GetConstructor(Type objType, Type[] argTypes)
        {
            Func<T1, T2, T3, object> constructor;
            if (!constructorCache.TryGetValue(objType, out constructor))
            {
                constructor = (Func<T1, T2, T3, object>)FastActivator.BuildConstructorDelegate(objType, typeof(Func<T1, T2, T3, object>), argTypes);
                constructorCache.Add(objType, constructor);
            }
            return constructor;
        }
    }

    Nogle resultater nedenfor. Bemærk dette er for at skabe 1 million objekter og tider i millisekunder igen:

    Activator.CreateInstance(objType) - parameterless constructor: 153
    FastActivator.CreateInstance(objType) - parameterless constructor: 86
    Using FastActivator.GetConstructor and calling it repeatedly - parameterless constructor: 34
    Activator.CreateInstance(objType) with 1 constructor arg: 3183
    FastActivator.CreateInstance(objType) with 1 constructor arg: 257
    FastActivator.GetConstructor and calling it repeatedly with 1 constructor arg: 126
    Activator.CreateInstance(objType) with 3 constructor args: 4403
    FastActivator.CreateInstance(objType) with 3 constructor args: 640
    FastActivator.GetConstructor and calling it repeatedly with 3 constructor args : 405
    FastActivator.GetConstructor and calling it repeatedly with 3 constructor args; args created only once : 19
    Tak for jeres hårde arbejde, og test, i denne tråd ! Det er interessant at bemærke, at det i .NET 4.0 ‘System.RunTime ” er en metode, med underskrift: ‘CreateInstanceSlow(bool publicOnly, bool skipCheckThis, bool fillCache)’ … og ‘Activator’ har 25 overbelastninger for “CreateInstance’ metode.
    Hver gang jeg ser noget, som Func<object.. jeg føler mig trist..
    Hvorfor? Hvad er der galt med det træk ved sproget? Eller er det øverste niveau “objekt” type?
    ja, det sidste. Stor fan af generiske lægemidler. Jeg kender nogle gang uundgåelig.
    Jeg har finjusteret en kopi af denne FastActivator version for at bruge ConcurrentDictionary<TKey,TValue> med sin metode GetOrAdd(TKey Key, Func<TKey,TValue>)… Har ikke testet dets resultater endnu, men jeg formoder, at det vil være tættere på i performance hensyn til denne FastActivator end at Activator. Dette skaber en afhængighed på .Net 4+.

    OriginalForfatteren David Doran

  2. 4

    Jeg lægger dette op som den hidtil bedste højtydende objekt oprettelse fabrikken, så langt ved hjælp af den aktuelle (2010-01-11) svar, ifølge mine tests. Jeg lagde mærke til, at bruge cache fungerer bedst, når iterationer er et sted under 99,999. Hvis du kommer til at indlæse mere end 99,999 det er bedst ikke at bruge cache. Fordi dette kunne være tilfældet, jeg har skabt noget, der vil tillade dig at bruge cache eller ikke. Mit nuværende projekt vil nogle gange belastning millioner af plader og andre gange kun en belastning. Anyways, jeg lægger dette derud for at se hvad dine tanker er. Bemærk, at koden nedenfor er for ctor ‘ s, der har 1 arg, ville man være nødt til at oprette en lignende fabrik for mere end 1 arg ctor.

    
    //code updated 2010-06-01
    //class that creates comment objects
    public class CreatesSomeObject
    {
        //method that creates a comment object
        public void CreateComment()
        {
    
            //Method 1 (without cache)
            Comment comment1 = ObjectFactoryFactory<Comment, ObjectId>
                .CreateObject.Invoke(new ObjectId());
    
            //Method 2 (with cache)
            Comment comment2 = ObjectFactoryFactory<Comment, ObjectId>
                .CreateObjectWithCache.Invoke(new ObjectId());
    
            //Method 3 (without helper factory ObjectFactoryFactory)
            Comment comment3 = ObjectFactory<Comment, ObjectId>
                .CreateObject.Invoke(new ObjectId());
        }
    }
    
    //This is optional class. Just helps in creating objects when
    //a cache is needed or not needed.
    public static class ObjectFactoryFactory<T, P1> where T : class
    {
        static Hashtable cache = Hashtable.Synchronized(new Hashtable());
    
        public static Func<P1, T> CreateObject
        {
            get { return ObjectFactory<T, P1>.CreateObject; }
        }
    
        public static Func<P1, T> CreateObjectWithCache
        {
            get
            {
                return ObjectFactory<T, P1>.UseCache(cache);
            }
        }
    }
    
    //Main object creation factory class.
    public static class ObjectFactory<T, P1> where T : class
    {
    
        static Func<P1, T> _createObject;
    
        public static Func<P1, T> CreateObject
        {
            get
            {
                if (_createObject != null) return _createObject;
                _createObject = CreateDelegate();
                return _createObject;
            }
        }
    
        static Func<P1, T> CreateDelegate()
        {
            Type objType = typeof(T);
            Type[] types = new[] { typeof(P1) };
            var dynMethod = new DynamicMethod("DM$OBJ_FACTORY_" + 
                objType.Name, objType, types, objType);
            ILGenerator ilGen = dynMethod.GetILGenerator();
            //if need more than 1 arg add another Ldarg_x
            //you'll also need to add proper generics and 
            //CreateDelegate signatures
            ilGen.Emit(OpCodes.Ldarg_0);
            ilGen.Emit(OpCodes.Newobj, objType.GetConstructor(types));
            ilGen.Emit(OpCodes.Ret);
            return (Func<P1, T>)dynMethod.CreateDelegate(typeof(Func<P1, T>));
        }
    
        public static Func<P1, T> UseCache(Hashtable cache) 
        { 
            Type t = typeof(T);
            Func<P1, T> c = cache[t] as Func<P1, T>;
            if (c == null) 
            { 
                lock (cache.SyncRoot) 
                {
                    c = cache[t] as Func<P1, T>;
                    if (c != null) 
                    { 
                        return c; 
                    } 
                    c = CreateDelegate(); 
                    cache.Add(t, c); 
                } 
    
            } 
            return c; 
        }
    }
    jeg har ikke hørt nogen kommentarer til et stykke tid, så jeg vil mærke dette som det bedste svar for så vidt.
    Fremragende. Præcis hvad jeg var på udkig efter undtagelse ikke i VB :-). Du mangler 2 semi-kolon i UseCache(). Det ville også gøre eksemplet mere komplet, hvis du har tilføjet en Kommentar, og ObjectId klasser.
    Sætter virkelig pris på jeres hårde arbejde og test i denne tråd ! Ville være interessant at se, hvordan RepDbg ‘ s strategi, som udføres i modsætning til de øvrige tiltag her.

    OriginalForfatteren thames

  3. 1

    Jeg skrev for et stykke tid siden. Det er ved hjælp af den .NET 3,5 Linq Udtryk træer snarere end som udsender IL, som er næsten lige så hurtigt, og mere vedligeholdelsesvenlig. Det kan tage op til 4 konstruktør argumenter.

    Brug constructor argumenter, som du ønsker at gøre, kan være en smule langsommere, men på grund af at kigge op constructor baseret på det argument, typer, men det er stadig meget hurtigere end med refleksion. Også med IL-emission der skulle være nogle opslag, tror jeg.

    Du er nødt til at angive den nøjagtige type, som du ønsker at konstruere, da det ikke IOC/DI container. Måske kan du udvide og tilpasse det til dine behov.

    //usage:
    Cat myCat = Instantiator<Cat>.New("furry", /* isCute*/ true);
    
    using System;
    using System.Diagnostics;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Reflection;
    
    static public class Instantiator<TInstance>
    {
        static Instantiator()
        {
            Debug.Assert(typeof(TInstance).IsValueType || (typeof(TInstance).IsClass && !typeof(TInstance).IsAbstract),
                    String.Concat("The type ", typeof(TInstance).Name, " is not constructable."));
        }
    
        static public TInstance New()
        {
            return InstantiatorImpl.CtorFunc();
        }
    
        static public TInstance New<TA>(TA valueA)
        {
            return InstantiatorImpl<TA>.CtorFunc(valueA);
        }
    
        static public TInstance New<TA, TB>(TA valueA, TB valueB)
        {
            return InstantiatorImpl<TA, TB>.CtorFunc(valueA, valueB);
        }
    
        static public TInstance New<TA, TB, TC>(TA valueA, TB valueB, TC valueC)
        {
            return InstantiatorImpl<TA, TB, TC>.CtorFunc(valueA, valueB, valueC);
        }
    
        static public TInstance New<TA, TB, TC, TD>(TA valueA, TB valueB, TC valueC, TD valueD)
        {
            return InstantiatorImpl<TA, TB, TC, TD>.CtorFunc(valueA, valueB, valueC, valueD);
        }
    
        static private Expression<TDelegate> CreateLambdaExpression<TDelegate>(params Type[] argTypes)
        {
            Debug.Assert(argTypes != null);
    
            ParameterExpression[] paramExpressions = new ParameterExpression[argTypes.Length];
    
            for (int i = 0; i < paramExpressions.Length; i++)
            {
                paramExpressions[i] = Expression.Parameter(argTypes[i], String.Concat("arg", i));
            }
    
            ConstructorInfo ctorInfo = typeof(TInstance).GetConstructor(argTypes);
            if (ctorInfo == null)
            {
                throw new ArgumentException(String.Concat("The type ", typeof(TInstance).Name, " has no constructor with the argument type(s) ", String.Join(", ", argTypes.Select(t => t.Name).ToArray()), "."),
                        "argTypes");
            }
    
            return Expression.Lambda<TDelegate>(Expression.New(ctorInfo, paramExpressions), paramExpressions);
        }
    
        static private class InstantiatorImpl
        {
            static public readonly Func<TInstance> CtorFunc = Expression.Lambda<Func<TInstance>>(Expression.New(typeof(TInstance))).Compile();
        }
    
        static private class InstantiatorImpl<TA>
        {
            static public readonly Func<TA, TInstance> CtorFunc = Instantiator<TInstance>.CreateLambdaExpression<Func<TA, TInstance>>(typeof(TA)).Compile();
        }
    
        static private class InstantiatorImpl<TA, TB>
        {
            static public readonly Func<TA, TB, TInstance> CtorFunc = Instantiator<TInstance>.CreateLambdaExpression<Func<TA, TB, TInstance>>(typeof(TA), typeof(TB)).Compile();
        }
    
        static private class InstantiatorImpl<TA, TB, TC>
        {
            static public readonly Func<TA, TB, TC, TInstance> CtorFunc = Instantiator<TInstance>.CreateLambdaExpression<Func<TA, TB, TC, TInstance>>(typeof(TA), typeof(TB), typeof(TC)).Compile();
        }
    
        static private class InstantiatorImpl<TA, TB, TC, TD>
        {
            static public readonly Func<TA, TB, TC, TD, TInstance> CtorFunc = Instantiator<TInstance>.CreateLambdaExpression<Func<TA, TB, TC, TD, TInstance>>(typeof(TA), typeof(TB), typeof(TC), typeof(TD)).Compile();
        }
    }

    Har teh funz med det! :->

    tak! jeg vil give det en chance. jeg har aldrig tænkt over linq udtryk træer.
    Jeg har opdateret Instantiator fordi jeg refactored og forenklet det til en masse. Jeg har bemærket, jeg kan gøre, uden at ordbog og udnytte compiler ‘ s generiske mekanismer, i stedet for at holde henvisninger til de indsamlede lambda-udtryk. Vi laver alle fejltagelser i fortiden, og vi lærer med tiden. 😉 … Men, udtryk træer stadig tage et stykke tid at kompilere, men når initialiseret, det kommer så tæt som du kan få til native kode.
    jeg vil tage et kig. tak for opdateringen!
    Ser ud som om opdateringen har fremskynde det op i min test. FRA: Instantiator<Mitobjekt>.Nye(string,int): 00:00:00.0249225 TIL: Instantiator<Mitobjekt>.Nye(string,int): 00:00:00.0075623
    Dette er det bedste svar her. Hvorfor skulle det ikke u bruge den generelle mønster i InstantiatorImpl sag? Hvorfor separat kompilering af træ til det?

    OriginalForfatteren herzmeister

  4. 1

    Jeg havde ingen idé om, at nye T() var langsom i en generisk klasse. Mit problem er virkelig noget andet men – hvis jeg vidste, hvad de T var ved compile tid jeg ville være fint, men jeg ønsker en hurtig måde til at instantiere en klasse, der er specificeret af oplysninger om konfiguration (dvs strenge holder samling /klasse navne) på runtime. Jeg bruger en cache af Type objekter for at indlæse forsamling og finde den type, der er det kun en gang, så den sidste hurdle er at hurtigt at oprette tilfælde, som er omfattet af mine tidligere indlæg.

    Under alle omstændigheder, er følgende fra min konstatering af, at .NET 4.0 er hurtigere på denne slags ting, jeg testede med en version af dit eksempel, ringer hver CreateNewInstxxx metode 1,000,000 gange. Tider i millisekunder:

    class GenericClass<T> where T : new()
    {
        Func<T> ClassFactory;    
        public GenericClass(Func<T> classFactory)    
        {        
            this.ClassFactory = classFactory;    
        }    
        public T CreateNewInstQuick()    
        {        
            return ClassFactory(); //Calls the quick IL mnemonic 'newobj'   
        }
        public T CreateNewInstStd()
        {
            return new T(); //<- calls the slow Activator.CreateInstance() in IL
        }
    }
    
    .NET 3.5
    CreateNewInstQuick: 35
    CreateNewInstStd: 1298
    
    .NET 4.0
    CreateNewInstQuick: 29
    CreateNewInstStd: 165

    Så ja .NET 4.0 er meget hurtigere end tidligere her. Koden genereret af oversætteren for CreateNewInstStd () – metoden ser ud som om i begge tilfælde, så ser det ud til speedup er ned til forbedret Activator.CreateInstance<T>() metode:

    public T CreateNewInstStd()
    {
        T t1 = default(T);
        if (t1 != null)
        {
            T t2 = default(T);
            return t2;
        }
        return Activator.CreateInstance<T>();
    }

    Edit: til At tilføje, at resultaterne er nogenlunde den samme, når GenericClass er begrænset til reference-typer via denne:

    class GenericClass<T> where T :  class, new()

    og i dette tilfælde compiler-genereret kode, er så blot dette:

    public T CreateNewInstStd()
    {
        return Activator.CreateInstance<T>();
    }
    Jeg vil gerne, at CreateNewInstStd være lige return default(T) != null ? default(T) : Activator.CreateInstance<T>() 🙂

    OriginalForfatteren David Doran

  5. 1

    Jeg typisk bruge følgende metode til helt at undgå Activator.CreateInstance() i generiske klasser.

    Jeg kræver en Func<T> uddelegere til at være gået til det den konstruktøren. Klassen vil kalde det, hvis der er behov for at instantiere en ny instans af T. Dette vil gøre instantiering så hurtigt som i en ikke-generiske klasse og er mere enkel og elegant IMO end en sammensat klasse, der udsender IL. En anden upside (VS New T()) ved hjælp af denne metode er, at du kan instantiere en klasse med en parametriserede constructor.

    Edit: jeg har opdateret koden med en parametriserede constructor eksempel per BillW anmodning.

    class GenericClass<T>
    {
        public GenericClass(Func<T> classFactory)
        {
            this.ClassFactory = classFactory;
        }
        Func<T> ClassFactory;
    
        public void CreateNewInstDemo()
        {
            //T NewObject = New T(); //<- calls the slow Activator.CreateInstance() in IL
            T NewObject = ClassFactory(); //Calls the quick IL mnemonic 'newobj'
        }
    }
    class GenericClassParamsDemo<T>
    {
        public GenericClassParamsDemo(Func<int, T> classFactory)
        {
            this.ClassFactory = classFactory;
        }
        Func<int, T> ClassFactory;
    
        public void CreateNewInstDemo()
        {
            T NewObject = ClassFactory(5); //Calls the quick IL mnemonic 'newobj'
        }
    }
    class ClassToCreate
    {
        public int Number { get; set; }
        public ClassToCreate()
        {
            //Default constructor
        }
        public ClassToCreate(int number)
        {
            //Constructor With Parameter
            this.Number = number;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            GenericClass<ClassToCreate> gc =
            new GenericClass<ClassToCreate>(() => { return new ClassToCreate(); });
            //Call method which uses delegate to create a new instance
            gc.CreateNewInstDemo();
    
            GenericClassParamsDemo<ClassToCreate> gcParams =
            new GenericClassParamsDemo<ClassToCreate>((number) => { return new ClassToCreate(number); });
             //Call method which uses delegate to create a new instance with params
            gcParams.CreateNewInstDemo();
        }
    }
    Interessant, og jeg upvoted dette svar. Jeg ville sætte pris på det, hvis du ville vise et eksempel på, hvordan du vil bruge denne strategi til at oprette en ny klasse med parametre.

    OriginalForfatteren RepDbg

  6. 0

    Har dine profiler vist dig, at en stor del af tiden er tilbragt i Aktivator.CreateInstance() (og ikke nogen constructor kaldes ved det), hvis du ikke bruger den enkle metode. Hvis det ikke er tilfældet, skal du blot bruge Aktivator.CreateInstance. (Der synes ikke at være en generisk CreateInstance () – metoden med ctor argumenter, men der er en ikke-generisk).

    OriginalForfatteren erikkallen

  7. 0

    At følge @thames-svar konverteret til VB.NET.

    //' Original source: http://stackoverflow.com/questions/2024435/how-to-pass-ctor-args-in-activator-createinstance-or-use-il/2045313#2045313
    //' Converted to VB with: http://www.developerfusion.com/tools/convert/csharp-to-vb/
    //' code updated 2010-06-01
    //' class that creates comment objects
    Public Class CreatesSomeObject
    
        //' method that creates a comment object
        Public Sub CreateComment()
    
            //' Method 1 (without cache)
            Dim comment1 As Comment = ObjectFactoryFactory(Of Comment, ObjectId).CreateObject.Invoke(New ObjectId())
    
            //' Method 2 (with cache)
            Dim comment2 As Comment = ObjectFactoryFactory(Of Comment, ObjectId).CreateObjectWithCache.Invoke(New ObjectId())
    
            //' Method 3 (without helper factory ObjectFactoryFactory)
            Dim comment3 As Comment = ObjectFactory(Of Comment, ObjectId).CreateObject.Invoke(New ObjectId())
    
        End Sub
    
    End Class
    
    Public Class Comment
    
        Public Sub New(ByVal objectId As ObjectId)
        End Sub
    
    End Class
    
    Public Class ObjectId
    End Class
    
    //' This is optional class. Just helps in creating objects when
    //' a cache is needed or not needed.
    Public NotInheritable Class ObjectFactoryFactory(Of T As Class, P1)
    
        Private Sub New()
        End Sub
    
        Shared cache As Hashtable = Hashtable.Synchronized(New Hashtable())
    
        Public Shared ReadOnly Property CreateObject() As Func(Of P1, T)
            Get
                Return ObjectFactory(Of T, P1).CreateObject
            End Get
        End Property
    
        Public Shared ReadOnly Property CreateObjectWithCache() As Func(Of P1, T)
            Get
                Return ObjectFactory(Of T, P1).UseCache(cache)
            End Get
        End Property
    End Class
    
    //' Main object creation factory class.
    Public NotInheritable Class ObjectFactory(Of T As Class, P1)
    
        Private Sub New()
        End Sub
    
        Shared _createObject As Func(Of P1, T)
    
        Public Shared ReadOnly Property CreateObject() As Func(Of P1, T)
            Get
                If _createObject IsNot Nothing Then
                    Return _createObject
                End If
                _createObject = CreateDelegate()
                Return _createObject
            End Get
        End Property
    
        Private Shared Function CreateDelegate() As Func(Of P1, T)
    
            Dim objType As Type = GetType(T)
            Dim types As Type() = {GetType(P1)}
            Dim dynMethod = New DynamicMethod("DM$OBJ_FACTORY_" + objType.Name, objType, types, objType)
            Dim ilGen As ILGenerator = dynMethod.GetILGenerator()
    
            //' if need more than 1 arg add another Ldarg_x
            //' you'll also need to add proper generics and 
            //' CreateDelegate signatures
            ilGen.Emit(OpCodes.Ldarg_0)
            ilGen.Emit(OpCodes.Newobj, objType.GetConstructor(types))
            ilGen.Emit(OpCodes.Ret)
    
            Return DirectCast(dynMethod.CreateDelegate(GetType(Func(Of P1, T))), Func(Of P1, T))
    
        End Function
    
        Public Shared Function UseCache(ByVal cache As Hashtable) As Func(Of P1, T)
    
            Dim t As Type = GetType(T)
            Dim c As Func(Of P1, T) = TryCast(cache(t), Func(Of P1, T))
    
            If c Is Nothing Then
    
                SyncLock cache.SyncRoot
    
                    c = TryCast(cache(t), Func(Of P1, T))
                    If c IsNot Nothing Then
                        Return c
                    End If
                    c = CreateDelegate()
                    cache.Add(t, c)
    
                End SyncLock
    
            End If
    
            Return c
    
        End Function
    
    End Class

    Bemærk brugen //’ for kommentarer giver SÅ kode colorizer til at fungere korrekt. Efter at indsætte denne kode ind i dit projekt erstatte //’ med ‘.

    OriginalForfatteren Tim Murphy

  8. 0

    Jeg tror, den første ting du nødt til at ændre, er CreateObject uddelegere. Jeg ville gøre det på denne måde:

    public delegate object CreateObject(params object[] args);

    Så, her er en god måde at generere en CreateObject delegeret fra en ConstructorInfo (ved hjælp af udtryk træer):

    ///<summary>
    ///   Creates and compiles an Expression like this:
    ///(object[] args) =>
    ///(object)(
    ///    new {ConstructedType}(args[0], args[1], ...)
    ///)
    ///</summary>
    public static DynamicStaticMethod CreateConstructor(ConstructorInfo constructor)
    {
        if (constructor == null) throw new ArgumentNullException("constructor");
    
        //Create 'args' parameter expression
        ParameterExpression argsParameter = Expression.Parameter(typeof(object[]), "args");
    
        //Create body expression
        ParameterInfo[] constructorParams = constructor.GetParameters();
        Expression body = Expression.New(
            constructor,
            CreateParameterExpressions(constructorParams, argsParameter)
        );
    
        //Create and compile lambda
        var lambda = Expression.Lambda<CreateObject>(
            Expression.Convert(body, typeof(object)),
            argsParameter
        );
        return lambda.Compile();
    }

    En fuld udvidet version af denne compiler er i min personlige arkiv. Velkommen til at gennemse, hvad der er der.

    Opdatering: repository link ændret.

    jeg vil give ‘ en chance! tak!
    jeg kan have brug for mere information om den bedste måde at indlede CreateConstructor men denne metode ser ud til at tage den længste. Fra mine tests (ingen cache) 999 forekomster af det samme objekt tager lidt over 2 sekunder. Dette tager længere tid end Aktivator.CreateObject af ganske lidt.
    Aktivator.CreateInstance(Type)(string,int): 00:00:00.0139442 <br/>Instantiator<Mitobjekt>.Nye(string,int): 00:00:00.0249225 FastObjectFactory.CreateObjec (tøm cache): 00:00:00.0023258 FastObjectFactory.CreateObjec (cache): 00:00:00.0000439 LateBoundMethodFactory.CreateConstructor: 00:00:02.8543006
    Dette er i det væsentlige den samme som den Instantiator men uden caching, det er derfor, det ikke klarer sig godt. Caching af den samlede udtryk er afgørende. Det er derfor, 999 tilfælde, er stadig ikke nok til at overgå Aktivator. :-/ Kun efter 10000 du vil se effekten. Så det er værd at overveje, hvis ansøgningen er langlivet.
    godt at vide, da der vil være gange, at der vil være over 10.000. jeg vil gøre nogle test med større end 10k.

    OriginalForfatteren jpbochi

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *