لماذا تؤدي الطريقة العامة مع قيود T: الفئة إلى الملاكمة؟ [مكرر
سؤال
هذا السؤال لديه بالفعل إجابة هنا:
لماذا الطريقة العامة التي تقيد t على فئة سيكون لها تعليمات الملاكمة في رمز MSIL؟
لقد فوجئت بهذا الأمر منذ أن يتم تقييد T بالتأكيد على نوع مرجعي ، يجب ألا يحتاج الرمز الذي تم إنشاؤه إلى إجراء أي ملاكمة.
هنا رمز C#:
protected void SetRefProperty<T>(ref T propertyBackingField, T newValue) where T : class
{
bool isDifferent = false;
// for reference types, we use a simple reference equality check to determine
// whether the values are 'equal'. We do not use an equality comparer as these are often
// unreliable indicators of equality, AND because value equivalence does NOT indicate
// that we should share a reference type since it may be a mutable.
if (propertyBackingField != newValue)
{
isDifferent = true;
}
}
هنا هو IL المولد:
.method family hidebysig instance void SetRefProperty<class T>(!!T& propertyBackingField, !!T newValue) cil managed
{
.maxstack 2
.locals init (
[0] bool isDifferent,
[1] bool CS$4$0000)
L_0000: nop
L_0001: ldc.i4.0
L_0002: stloc.0
L_0003: ldarg.1
L_0004: ldobj !!T
L_0009: box !!T
L_000e: ldarg.2
L_000f: box !!T
L_0014: ceq
L_0016: stloc.1
L_0017: ldloc.1
L_0018: brtrue.s L_001e
L_001a: nop
L_001b: ldc.i4.1
L_001c: stloc.0
L_001d: nop
L_001e: ret
}
لاحظ ال مربع !! ر تعليمات.
لماذا يتم إنشاء هذا؟
كيف تتجنب هذا؟
المحلول
لا داعي للقلق بشأن أي تحط من الأداء من box
التعليمات لأنه إذا كانت وسيطتها نوع مرجعي ، box
التعليم لا شيء. على الرغم من أنه لا يزال غريبًا أن box
حتى تم إنشاء التعليمات (ربما كسراون/تصميم أسهل في توليد الكود؟).
نصائح أخرى
لست متأكدًا من السبب وراء وجود أي ملاكمة. إحدى الطرق الممكنة لتجنب الملاكمة هي عدم استخدامه. فقط إعادة الترجمة بدون الملاكمة. السابق:
.assembly recomp_srp
{
.ver 1:0:0:0
}
.class public auto ansi FixedPBF
{
.method public instance void .ctor() cil managed
{
}
.method hidebysig public instance void SetRefProperty<class T>(!!T& propertyBackingField, !!T newValue) cil managed
{
.maxstack 2
.locals init ( bool isDifferent, bool CS$4$0000)
ldc.i4.0
stloc.0
ldarg.1
ldobj !!T
ldarg.2
ceq
stloc.1
ldloc.1
brtrue.s L_0001
ldc.i4.1
stloc.0
L_0001: ret
}
}
... إذا قمت بحفظه إلى ملف recomp_srp.msil ، يمكنك ببساطة إعادة الترجمة على هذا النحو:
ildasm /dll recomp_srp.msil
ويستمر بشكل جيد بدون الملاكمة في نهايتي:
FixedPBF TestFixedPBF = new FixedPBF();
TestFixedPBF.SetRefProperty<string>(ref TestField, "test2");
... بالطبع ، قمت بتغييره من الحماية إلى الأماكن العامة ، ستحتاج إلى إعادة التغيير مرة أخرى وتقديم بقية التنفيذ.
أعتقد أن هذا يهدف إلى التصميم. أنت لا تقيد T في فئة معينة ، لذا فمن المرجح أن تصل إلى الاعتراض. وبالتالي لماذا ترى IL تشمل الملاكمة.
سأحاول هذا الرمز مع T: againclass
متابعة على بضع نقاط. بادئ ذي بدء ، يحدث هذا الخطأ لكلا الطريقتين في أ فئة عامة مع القيد where T : class
و أيضا طرق عامة مع هذا القيد نفسه (في فئة عامة أو غير جنسية). لا يحدث للطريقة غير المتطابقة (متطابقة) التي تستخدم Object
بدلاً من T
:
// static T XchgNullCur<T>(ref T addr, T value) where T : class =>
// Interlocked.CompareExchange(ref addr, val, null) ?? value;
.locals init (!T tmp)
ldarg addr
ldarg val
ldloca tmp
initobj !T
ldloc tmp
call !!0 Interlocked::CompareExchange<!T>(!!0&, !!0, !!0)
dup
box !T
brtrue L_001a
pop
ldarg val
L_001a:
ret
// static Object XchgNullCur(ref Object addr, Object val) =>
// Interlocked.CompareExchange(ref addr, val, null) ?? value;
ldarg addr
ldarg val
ldnull
call object Interlocked::CompareExchange(object&, object, object)
dup
brtrue L_000d
pop
ldarg val
L_000d:
ret
لاحظ بعض المشكلات الإضافية مع المثال الأول. بدلا من ببساطة ldnull
لدينا غريب initobj
استدعاء لا جدال فيه استهداف متغير محلي فائض tmp
.
الأخبار السارة ومع ذلك ، ألمح هنا, ، هو أن أيا من هذا يهم. على الرغم من الاختلافات في رمز IL الذي تم إنشاؤه للحصول على مثالين أعلاه ، x64 jit يولد رمز متطابق تقريبا لهم. النتيجة التالية هي لـ .NET Framework 4.7.2 وضع الإصدار مع التحسين "غير قاتمة".