c# - إنشاء سريع للكائنات بدلاً من Activator.CreateInstance (نوع)





reflection reflection.emit (6)


قد يساعد هذا: لا تستخدم Activator.CreateInstance أو ConstructorInfo.Invoke ، استخدم تعبيرات lambda المترجمة :

// Make a NewExpression that calls the ctor with the args we just created
NewExpression newExp = Expression.New(ctor, argsExp);                  

// Create a lambda with the New expression as body and our param object[] as arg
LambdaExpression lambda = Expression.Lambda(typeof(ObjectActivator), newExp, param);            


// Compile it
ObjectActivator compiled = (ObjectActivator)lambda.Compile();

أحاول تحسين أداء طلبنا. لدينا الكثير من المكالمات Activator.CreateInstance التي تسبب بعض الحزن.

نحن نجعل الكثير من الطبقات تقوم على واجهة (ITabDocument) وبعد النظر حولي ، فكرت في استخدام هذا الكود:

الرمز ليس أفضل (أبطأ هامشيا أبطأ) من استخدام رمز Activator.CreateInstance كان لدينا.

    public static Func<T> CreateInstance<T>(Type objType) where T : class, new()
    {
        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);
        return (Func<T>)dynMethod.CreateDelegate(typeof(Func<T>));
    }

أنا أتساءل لماذا هذا ، كل ما أفعله هو:

ITabDocument document = CreateInstance<ITabDocument>(Type.GetType("[Company].Something"));

هل هناك طريقة أفضل لإنشاء الأشياء التي من شأنها أن تساعد في ما سبق؟ من الصعب قليلا عندما كنت غير متأكد من نوع ملموسة.




قمت ببعض المقارنات بين هذه (أقوم بتدوين التفاصيل الدنيا):

public static T Instance() //~1800 ms
{
    return new T();
}

public static T Instance() //~1800 ms
{
    return new Activator.CreateInstance<T>();
}

public static readonly Func<T> Instance = () => new T(); //~1800 ms

public static readonly Func<T> Instance = () => 
                                 Activator.CreateInstance<T>(); //~1800 ms

//works for types with no default constructor as well
public static readonly Func<T> Instance = () => 
               (T)FormatterServices.GetUninitializedObject(typeof(T)); //~2000 ms


public static readonly Func<T> Instance = 
     Expression.Lambda<Func<T>>(Expression.New(typeof(T))).Compile();  
     //~50 ms for classes and ~100 ms for structs

وكما يقول القرص المضغوط ، فإن التعبير المجمع هو الأسرع ، وبهامش كبير. كافة الطرق باستثناء (T)FormatterServices.GetUninitializedObject(typeof(T)) تعمل فقط مع أنواع منشئ افتراضي.

والتخزين المؤقت للمفوض الناتج الناتجة مترجم عندما يكون لديك فئة ثابتة لكل نوع عام. مثل:

public static class New<T> where T : new()
{
    public static readonly Func<T> Instance = Expression.Lambda<Func<T>>
                                              (
                                               Expression.New(typeof(T))
                                              ).Compile();
}

لاحظ القيد new . اتصل بأي شيء

MyType me = New<MyType>.Instance();

باستثناء المرة الأولى التي يتم فيها تحميل الصف في الذاكرة ، سيكون التنفيذ أسرع.

للحصول على فصل دراسي يتعامل مع كلا النوعين مع مُنشئ افتراضي وبدون ، اتبعت منهجًا هجينًا ، من هنا :

public static class New<T>
{
    public static readonly Func<T> Instance = Creator();

    static Func<T> Creator()
    {
        Type t = typeof(T);
        if (t == typeof(string))
            return Expression.Lambda<Func<T>>(Expression.Constant(string.Empty)).Compile();

        if (t.HasDefaultConstructor())
            return Expression.Lambda<Func<T>>(Expression.New(t)).Compile();

        return () => (T)FormatterServices.GetUninitializedObject(t);
    }
}

public static bool HasDefaultConstructor(this Type t)
{
    return t.IsValueType || t.GetConstructor(Type.EmptyTypes) != null;
}

سوف تتعامل مع أنواع القيم أيضا بطريقة فعالة.

لاحظ أنه ستفشل (T)FormatterServices.GetUninitializedObject(t) أجل string . ومن ثم فإن المعالجة الخاصة للسلسلة تكون في مكانها لإرجاع السلسلة الفارغة.




ربما كنت تحصل على بعض النفقات العامة من جيل من نفس الرمز.

يقوم ILGenerator بإنشاء كود ILGenerator بشكل ديناميكي.

قم بإنشاء خريطة أو Dictionary للأنواع التي استخدمتها بالفعل ، والحفاظ على طريقة المصنع التي تم إنشاؤها لهذا النوع.




طريقة عامة لبناء المندوبين ، استدعاء منشئ مباشرة. يقوم تلقائيًا بالبحث عن المنشئ في نوع معين مع توقيع نوع المفوض المعطى وإنشاء المفوض من ذلك النوع. الرمز هنا:

/// <summary>
/// Reflective object construction helper.
/// All methods are thread safe.
/// </summary>
public static class Constructor
{
    /// <summary>
    /// Searches an instanceType constructor with delegateType-matching signature and constructs delegate of delegateType creating new instance of instanceType.
    /// Instance is casted to delegateTypes's return type. 
    /// Delegate's return type must be assignable from instanceType.
    /// </summary>
    /// <param name="delegateType">Type of delegate, with constructor-corresponding signature to be constructed.</param>
    /// <param name="instanceType">Type of instance to be constructed.</param>
    /// <returns>Delegate of delegateType wich constructs instance of instanceType by calling corresponding instanceType constructor.</returns>
    public static Delegate Compile(Type delegateType,Type instanceType)
    {
        if (!typeof(Delegate).IsAssignableFrom(delegateType))
        {
            throw new ArgumentException(String.Format("{0} is not a Delegate type.",delegateType.FullName),"delegateType");
        }
        var invoke = delegateType.GetMethod("Invoke");
        var parameterTypes = invoke.GetParameters().Select(pi => pi.ParameterType).ToArray();
        var resultType = invoke.ReturnType;
        if(!resultType.IsAssignableFrom(instanceType))
        {
            throw new ArgumentException(String.Format("Delegate's return type ({0}) is not assignable from {1}.",resultType.FullName,instanceType.FullName));
        }
        var ctor = instanceType.GetConstructor(
            BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, parameterTypes, null);
        if(ctor == null)
        {
            throw new ArgumentException("Can't find constructor with delegate's signature","instanceType");
        }
        var parapeters = parameterTypes.Select(Expression.Parameter).ToArray();

        var newExpression = Expression.Lambda(delegateType,
            Expression.Convert(Expression.New(ctor, parapeters), resultType),
            parapeters);
        var @delegate = newExpression.Compile();
        return @delegate;
    }
    public static TDelegate Compile<TDelegate>(Type instanceType)
    {
        return (TDelegate) (object) Compile(typeof (TDelegate), instanceType);
    }
}

هو جزء من مصادر مشروع Yappi . باستخدامه يمكنك بناء مندوب استدعاء أي منشئ من نوع معين ، بما في ذلك منشئ مع المعلمات (باستثناء المعلمات المرجع والخروج).

استخدام العينة:

var newList = Constructor.Compile<Func<int, IList<String>>>(typeof (List<String>));
var list = newList(100);

بعد إنشاء المندوب ، قم بتخزينه في مكان ما في قاموس ثابت أو في حقل ثابت من الصف بمعلمة عامة. لا تقم بإنشاء مندوب جديد في كل مرة. استخدم مفوضاً واحدًا لإنشاء مثيلات متعددة من النوع المحدد.




المشكلة هي أنك إذا كنت ستدعو CreateInstance مراراً وتكراراً بدلاً من حفظ النتيجة في مكان ما واستخدام تلك النتيجة مراراً وتكراراً ، فيجب عليك المضي قدماً والتخزين المؤقت داخلها.

internal static class DelegateStore<T>{
     internal static IDictionnary<string, Func<T>> Store =new ConcurrentDictionary<string,Func<T>>();
}

public static T CreateInstance<T>(Type objType) where T : class
{
    Func<T> returnFunc;
    if(!DelegateStore<T>.Store.TryGetValue(objType.FullName, out returnFunc)){
        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);
        returnFunc =(Func<T>)dynMethod.CreateDelegate(typeof(Func<T>));
        DelegateStore<T>.Store[objType.FullName]=returnfunc;
    }
    return returnFunc();
}



هناك طريقة أخرى لتمرير الوسائط إلى CreateInstance من خلال المعلمات المسماة.

بناءً على ذلك ، يمكنك تمرير صفيف نحو CreateInstance . سيسمح لك ذلك بالحصول على 0 أو عدة وسائط.

public T CreateInstance<T>(params object[] paramArray)
{
  return (T)Activator.CreateInstance(typeof(T), args:paramArray);
}




c# reflection reflection.emit activator createinstance