ج # استدعاء وإرجاع كائن من طريقة ثابتة في إيل
-
09-12-2019 - |
سؤال
هذا هو امتداد للحلول المقدمة هنا.لقد خلق طريقة ثابتة أن يعود لي كائن.هدفي ، هو كتابة طريقة ديناميكية لنوع أعرفه في وقت التشغيل لإرجاع الكائن الذي تعود إليه هذه الطريقة الثابتة.كود بلدي حتى الآن:
// 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
كائن معروف بشكل ثابت إلى طريقة كنت استدعاء حيوي.هذا ممكن ، ولكن عليك أن تقفز من خلال اثنين من الأطواق.
الحصول على إشارة إلى
MethodInfo
عن الطريقةType.GetTypeFromHandle
:MethodInfo getTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle");
استخدام الأسطر التالية من ايل لدفع الخاص بك
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);
سلوك المكدس الانتقالي لهذا الرمز هو:
دفع
RuntimeTypeHandle
المقابلة إلى المحددType
الرجوع إلى المكدس باستخدامOpcodes.Ldtoken
.استدعاء
getTypeFromHandle
الذي ينبثق مقبض النوع من المكدس ، ويدفع الفعليSystem.Type
على المكدس.استدعاء طريقة ثابتة ، والتي سوف البوب
Type
حجة الخروج من المكدس ودفع قيمة الإرجاع من الطريقة الخاصة بك على المكدس.العودة من الطريقة.
نصائح أخرى
قد تكون أشجار التعبير حلا أفضل هنا.من السهل جدا إنشاء ملف 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>>
وبناء عليه مرة واحدة فقط (كما تجميع التعبيرات مرارا وتكرارا هو مضيعة لموارد النظام).