سؤال

هذا هو امتداد للحلول المقدمة هنا.لقد خلق طريقة ثابتة أن يعود لي كائن.هدفي ، هو كتابة طريقة ديناميكية لنوع أعرفه في وقت التشغيل لإرجاع الكائن الذي تعود إليه هذه الطريقة الثابتة.كود بلدي حتى الآن:

 // type builder and other prep stuff removed for sake of space and reading

private void EmitReferenceMethodBody(Type returnType)
{
    MethodBuilder builder =
    typeBuilder.DefineMethod(
                    method.Name,
                    MethodAttributes.Virtual | MethodAttributes.Public,
                    method.CallingConvention,
                    method.ReturnType,
                    typeArray1);
    builder.InitLocals = true;
    ILGenerator gen = builder.GetILGenerator();
    MethodInfo getStoredObject = typeof(ObjectStore).GetMethod("GetStoredObject",                  BindingFlags.Public | BindingFlags.Static);        
    MethodInfo getTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle");            

    gen.Emit(OpCodes.Ldtoken, returnType);
    gen.Emit(OpCodes.Call, getTypeFromHandle);
    gen.Emit(OpCodes.Call, getStoredObject);
    gen.Emit(OpCodes.Ret);   
}

يستدعي الرمز المحدث الآن الطريقة ولكن يبدو أنه يمر بنوع النوع الذي تم إنشاؤه ديناميكيا بدلا من نوع العائد المتغير.

هل كانت مفيدة؟

المحلول

مشكلة واحدة على الأقل هي أنك تدفع المرجع" هذا" (OpCodes.Ldarg_0) على المكدس على الرغم من أنه لم برزت (منذ كنت استدعاء ثابت الطريقة).سأحاول إزالة هذا الخط ومعرفة ما إذا كان يتصرف بشكل أفضل.

مشكلة أخرى هي أنك تمر new Type[] { returnType } إلى EmitCall الطريقة.هذا مخصص للوسائط الاختيارية (params) وأظن أن طريقتك في الواقع لا تحتوي على أي معلمات.لذلك ، يجب عليك إزالة هذه الوسيطة أيضا.

تحرير:

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

  1. الحصول على إشارة إلى MethodInfo عن الطريقة Type.GetTypeFromHandle:

    MethodInfo getTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle");
    
  2. استخدام الأسطر التالية من ايل لدفع الخاص بك returnType على المكدس:

    gen.Emit(OpCodes.Ldtoken, returnType);
    gen.Emit(OpCodes.Call, getTypeFromHandle);
    

باختصار ، يجب أن تبدو التعليمات البرمجية الخاصة بك كما يلي:

MethodInfo getTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle");
gen.Emit(OpCodes.Ldtoken, returnType);
gen.Emit(OpCodes.Call, getTypeFromHandle);
gen.EmitCall(OpCodes.Call, getStoredObject);                
gen.Emit(OpCodes.Ret);

سلوك المكدس الانتقالي لهذا الرمز هو:

  1. دفع RuntimeTypeHandle المقابلة إلى المحدد Type الرجوع إلى المكدس باستخدام Opcodes.Ldtoken.

  2. استدعاء getTypeFromHandle الذي ينبثق مقبض النوع من المكدس ، ويدفع الفعلي System.Type على المكدس.

  3. استدعاء طريقة ثابتة ، والتي سوف البوب Type حجة الخروج من المكدس ودفع قيمة الإرجاع من الطريقة الخاصة بك على المكدس.

  4. العودة من الطريقة.

نصائح أخرى

قد تكون أشجار التعبير حلا أفضل هنا.من السهل جدا إنشاء ملف Func<Type, object> استخدام الكتابة الديناميكية من خلال التعبيرات.

ParameterExpression paramEx = Expression.Parameter(typeof(Type), "paramObject");
Expression callExpression = Expression.Call(typeof(ObjectStore), "GetStoredObject", null, paramEx);
Expression<Func<Type, object>> funcExpression = Expression.Lambda<Func<Type, object>>(callExpression, paramEx);
Func<Type, object> actualFunc = funcExpression.Compile();

الآن إذا كان أوبجكتستور يتطلب معلمات عامة ، يمكنك استبدال typeof(ObjectStore) مع typeof(ObjectStore).MakeGenericType(returnType).إذا كان GetStoredObject وظيفة نفسها تتطلب معلمات عامة ، ثم يمكنك تغيير null في ال Expression.Call بيان إلى new Type[] { returnType }.إذا تم إنشاء هذا مرة واحدة لكل نوع تمر به وتخطط لاستخدام هذا كثيرا، فقد يكون من الجيد تخزين هذه الوظائف مؤقتا في ملف Dictionary<Type, Func<Type, object>> وبناء عليه مرة واحدة فقط (كما تجميع التعبيرات مرارا وتكرارا هو مضيعة لموارد النظام).

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top