لماذا تؤدي الطريقة العامة مع قيود T: الفئة إلى الملاكمة؟ [مكرر

StackOverflow https://stackoverflow.com/questions/1400414

  •  05-07-2019
  •  | 
  •  

سؤال

هذا السؤال لديه بالفعل إجابة هنا:

لماذا الطريقة العامة التي تقيد 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 وضع الإصدار مع التحسين "غير قاتمة".

enter image description here

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