سؤال

ما هو الفرق بين CIL تعليمات "الدعوة" و "Callvirt"?

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

المحلول

وcall هو لاستدعاء الأساليب غير ظاهري، ثابتة، أو الطبقة المتفوقة، أي الهدف من الدعوة هو لا يخضع للتجاوز. callvirt هو لاستدعاء الأساليب الظاهرية (بحيث إذا this هو فئة فرعية أن يتجاوز الأسلوب، ويسمى نسخة فرعية بدلا من ذلك).

نصائح أخرى

عند وقت التشغيل ينفذ call تعليمات إنه إجراء مكالمة إلى دقيق قطعة من التعليمات البرمجية (الأسلوب).ليس هناك سؤال حول حيث كان موجودا. مرة واحدة IL تم JITted ، مما أدى إلى رمز الجهاز في الموقع غير مشروط jmp التعليمات.

وعلى النقيض من ذلك ، callvirt التدريس المستخدمة في الاتصال الظاهري وسائل متعددة الأشكال الطريق.الموقع الدقيق الطريقة البرمجية يجب أن تحدد في وقت التشغيل لكل الاحتجاج.مما أدى JITted رمز ينطوي على بعض المراوغة من خلال vtable الهياكل.وبالتالي الدعوة أبطأ في تنفيذ, لكنه أكثر مرنة ويتيح متعددة الأشكال المكالمات.

علما أن المترجم يمكن أن تنبعث من call تعليمات عن طرق افتراضية.على سبيل المثال:

sealed class SealedObject : object
{
   public override bool Equals(object o)
   {
      // ...
   }
}

النظر في استدعاء التعليمات البرمجية:

SealedObject a = // ...
object b = // ...

bool equal = a.Equals(b);

في حين System.Object.Equals(object) هو أسلوب ظاهري في هذا الاستخدام لا توجد وسيلة بالنسبة الزائد من Equals الطريقة موجودة. SealedObject هو مختوم في الدرجة لا يمكن أن يكون فرعية.

لهذا السبب،.صافي sealed الطبقات يمكن أن يكون أفضل طريقة إرسال الأداء من غير مختومة نظرائهم.

تحرير: تبين أنني كنت على خطأ.C# compiler لا يمكن أن تجعل غير مشروط القفز إلى الأسلوب موقع لأن الكائن المرجعي (قيمة this ضمن الأسلوب) قد تكون فارغة.بدلا تنبعث منه callvirt الذي لا باطل تحقق ويلقي إذا لزم الأمر.

وهذا ما يفسر في الواقع غريب رمز وجدت في .NET framework باستخدام عاكس:

if (this==null) // ...

فمن الممكن مترجم تنبعث منها يمكن التحقق منها رمز يحتوي على قيمة خالية عن this مؤشر (local0) ، إلا أن ديوان الخدمة المدنية لا يفعل هذا.

لذا أعتقد call يستخدم فقط لفئة أساليب ثابتة و البنيات.

تعطى هذه المعلومات الآن يبدو لي أن sealed هي فقط مفيدة API الأمن.وجدت سؤال آخر يبدو أن أقترح هناك أي مزايا الأداء إلى ختم الفصول الدراسية.

تحرير 2: هناك ما هو أكثر مما يبدو عليه.على سبيل المثال التعليمات البرمجية التالية تنبعث call التعليمات:

new SealedObject().Equals("Rubber ducky");

من الواضح في هذه الحالة لا يكون هناك أي فرصة أن الكائن سبيل المثال يمكن أن تكون فارغة.

ومن المثير للاهتمام, في بناء تصحيح التعليمات البرمجية التالي تنبعث callvirt:

var o = new SealedObject();
o.Equals("Rubber ducky");

هذا هو لأنه يمكنك تعيين نقطة توقف على السطر الثاني و تعديل قيمة o.في إصدار يبني أتصور أن الدعوة ستكون call بدلا من callvirt.

للأسف جهاز الكمبيوتر الخاص بي حاليا خارج العمل ، ولكن سوف تجربة مع هذا مرة واحدة مرة أخرى.

لهذا السبب،.صافي مختومة الطبقات يمكن أن يكون أفضل طريقة إرسال الأداء من غير مختومة نظرائهم.

للأسف هذا ليس هو الحال.Callvirt لا شيء آخر أن يجعل من المفيد.عندما كائن لديه طريقة تسمى على ذلك callvirt سوف تحقق إذا كان الكائن موجودا ، وإذا لم يكن يلقي NullReferenceException.اتصل ببساطة القفز إلى موقع الذاكرة حتى لو كان مرجع كائن ليس هناك محاولة تنفيذ وحدات البايت في هذا الموقع.

ما يعنيه هذا هو أن callvirt دائما يتم استخدامه من قبل برنامج التحويل البرمجي C# (غير متأكد حول VB) الطبقات ، وندعو دائما استخدام البنيات (لأنها لا يمكن أبدا أن تكون فارغة أو ثم subclassed).

تحرير ردا على وجه نواكس التعليق:نعم يبدو أنه يمكنك الحصول على المترجم أن تنبعث منها مكالمة لأي فئة ، ولكن فقط في حالة محددة جدا:

public class SampleClass
{
    public override bool Equals(object obj)
    {
        if (obj.ToString().Equals("Rubber Ducky", StringComparison.InvariantCultureIgnoreCase))
            return true;

        return base.Equals(obj);
    }

    public void SomeOtherMethod()
    {
    }

    static void Main(string[] args)
    {
        // This will emit a callvirt to System.Object.Equals
        bool test1 = new SampleClass().Equals("Rubber Ducky");

        // This will emit a call to SampleClass.SomeOtherMethod
        new SampleClass().SomeOtherMethod();

        // This will emit a callvirt to System.Object.Equals
        SampleClass temp = new SampleClass();
        bool test2 = temp.Equals("Rubber Ducky");

        // This will emit a callvirt to SampleClass.SomeOtherMethod
        temp.SomeOtherMethod();
    }
}

ملاحظة فئة لا يجب أن تكون مختومة لهذا العمل.

لذا يبدو المترجم سوف تنبعث منها مكالمة إذا كانت كل هذه الأمور صحيحة:

  • طريقة الاتصال فورا بعد إنشاء الكائن
  • هذه الطريقة لا تنفذ في قاعدة الطبقة

وفقا MSDN:

الاتصال:

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

CallVirt:

على callvirt التعليمات مكالمات متأخرة زمنيا الأسلوب على كائن.وهذا هو ، فإن الطريقة التي يتم اختيارها على أساس نوع وقت التشغيل من الكائنات بدلا من تجميع الوقت فئة مرئية في طريقة المؤشر.Callvirt يمكن استخدامها للاتصال سواء الافتراضي أساليب المثال.

وذلك أساسا, طرق مختلفة إلى استدعاء كائن سبيل المثال طريقة تنقضها أو لا:

الاتصال:متغير -> المتغير نوع الكائن -> طريقة

CallVirt:متغير -> كائن سبيل المثال -> الكائن نوع الكائن -> طريقة

وشيء واحد ربما يستحق مضيفا إلى الأجوبة السابقة هو، يبدو أن هناك واحد فقط وجها لكيف "دعوة IL" ينفذ في الواقع، وجهان لكيف "IL callvirt" تنفيذ.

ويأخذ هذا الإعداد عينة.

    public class Test {
        public int Val;
        public Test(int val)
            { Val = val; }
        public string FInst () // note: this==null throws before this point
            { return this == null ? "NO VALUE" : "ACTUAL VALUE " + Val; }
        public virtual string FVirt ()
            { return "ALWAYS AN ACTUAL VALUE " + Val; }
    }
    public static class TestExt {
        public static string FExt (this Test pObj) // note: pObj==null passes
            { return pObj == null ? "NO VALUE" : "VALUE " + pObj.Val; }
    }

أولا، الجسم CIL من FInst () وFEXT () 100٪ مطابقة، شفرة التشغيل إلى شفرة التشغيل (باستثناء وأعلن أن واحدة "مثيل" والآخر "ثابت") - ولكن، FInst () سوف الحصول على استدعاء مع "callvirt" وFEXT () مع "الدعوة"

.

وثانيا، سوف يطلق FInst () وFVirt () على حد سواء مع "callvirt" - على الرغم من واحد هو ظاهري ولكن البعض لا - ولكنها ليست "نفس callvirt" التي سوف تحصل حقا لتنفيذه.

وإليك ما يحدث تقريبا بعد JITting:

    pObj.FExt(); // IL:call
    mov         rcx, <pObj>
    call        (direct-ptr-to) <TestExt.FExt>

    pObj.FInst(); // IL:callvirt[instance]
    mov         rax, <pObj>
    cmp         byte ptr [rax],0
    mov         rcx, <pObj>
    call        (direct-ptr-to) <Test.FInst>

    pObj.FVirt(); // IL:callvirt[virtual]
    mov         rax, <pObj>
    mov         rax, qword ptr [rax]  
    mov         rax, qword ptr [rax + NNN]  
    mov         rcx, <pObj>
    call        qword ptr [rax + MMM]  

والفرق الوحيد بين "الدعوة" و "callvirt [مثلا]" هو أن "callvirt [مثلا]" يحاول عمدا للوصول بايت واحد من * pObj قبل أن يقوم باستدعاء مؤشر مباشرة لوظيفة سبيل المثال (لربما رمي استثناء "حق هناك وبعد ذلك").

وهكذا، إذا كنت منزعج من عدد المرات التي لديك لكتابة "جزء تدقيق" من

var d = GetDForABC (a, b, c);
var e = d != null ? d.GetE() : ClassD.SOME_DEFAULT_E;

وأنت لا يمكن أن تدفع "اذا كان (هذا == فارغة) عودة SOME_DEFAULT_E." أسفل إلى ClassD.GetE () نفسه (باسم "callvirt IL [مثلا]" دلالات تحظر لك أن تفعل هذا) ولكن أنت حر لدفعها إلى .GetE () إذا قمت بنقل .GetE () إلى وظيفة ملحق في مكان ما (كما في "دعوة IL" الدلالي يسمح لها - ولكن للأسف، فقدان القدرة على الوصول إلى أعضاء من القطاع الخاص وما إلى ذلك)

وقال ان تنفيذ "callvirt [مثلا]" لديها الكثير من القواسم المشتركة مع "الدعوة" من مع "callvirt [الظاهري]"، منذ قد يكون الأخير لتنفيذ المراوغة الثلاثي من أجل العثور على عنوان الدالة. (المراوغة لtypedef وقاعدة، ثم إلى قاعدة vtab-أو بعض واجهة، ثم إلى الفتحة الفعلية)

ويساعد هذا الأمل، بوريس

وفقط مشيرا إلى الإجابات أعلاه، أعتقد أنه قد تم إجراء التغيير منذ فترة طويلة مثل الخلفي أن تعليمات Callvirt IL سوف تحصل على إنشاء لجميع مثيل أساليب وطرق التدريس نداء IL سوف تحصل المتولدة عن أساليب ثابتة.

والمرجعي:

وPluralsight بالطبع "C الداخلية لل # اللغة - الجزء 1 من بارت دي سميت (فيديو - دعوة تعليمات والدعوة مداخن في CLR IL في القشر)

ووأيضا HTTPS: // بلوق .msdn.microsoft.com / ericgu / 2008/07/02 / لماذا-لا-ج-استخدام دائما، callvirt /

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