لماذا يقوم مترجم C# بإصدار تعليمات callvirt لاستدعاء أسلوب GetType()؟

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

سؤال

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

using System;

class Program
{
    static void Main()
    {
        Object o = new Object();
        o.GetType();

        // L_0001: newobj instance void [mscorlib]System.Object::.ctor()
        // L_0006: stloc.0 
        // L_0007: ldloc.0 
        // L_0008: callvirt instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()

        new Object().GetType();

        // L_000e: newobj instance void [mscorlib]System.Object::.ctor()
        // L_0013: call instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()
    }
}

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

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

المحلول

مجرد اللعب الآمن.

من الناحية الفنية، لا يقوم برنامج التحويل البرمجي C# بذلك دائماً يستخدم callvirt

بالنسبة للطرق والأساليب الثابتة المحددة في أنواع القيم، فإنه يستخدم call.يتم توفير الأغلبية عبر callvirt تعليمات إيل.

الفرق الذي أدى إلى تأرجح التصويت بين الاثنين هو حقيقة ذلك call يفترض أن "الكائن المستخدم لإجراء المكالمة" ليس فارغًا. callvirt من ناحية أخرى يتحقق من عدم وجود قيمة فارغة ويطرح NullReferenceException إذا لزم الأمر.

  • بالنسبة للطرق الثابتة، يكون الكائن كائن كتابة ولا يمكن أن يكون خاليًا.كما سبق لأنواع القيمة.لذلك call يستخدم لهم - أداء أفضل.
  • بالنسبة للآخرين، قرر مصممو اللغة الذهاب معهم callvirt لذلك يتحقق مترجم JIT من أن الكائن المستخدم لإجراء المكالمة ليس فارغًا.حتى بالنسبة لطرق المثيل غير الافتراضية ..لقد فضلوا السلامة على الأداء.

أنظر أيضا:يقوم جيف ريختر بعمل أفضل في هذا - في فصله "أنواع التصميم" في CLR عبر C# 2nd Ed

نصائح أخرى

يرى هذا منشور مدونة قديم بقلم إريك جونيرسون.

وهذا نص التدوينة:

لماذا يستخدم C# دائمًا callvirt؟

تم طرح هذا السؤال على اسم مستعار داخلي لـ C#، واعتقدت أن الإجابة ستكون ذات فائدة عامة.هذا على افتراض أن الإجابة صحيحة - لقد مر وقت طويل.

توفر لغة .NET IL كلاً من تعليمات call وcallvirt، مع استخدام callvirt لاستدعاء الوظائف الافتراضية.ولكن إذا نظرت إلى الكود الذي تنشئه C#، فسترى أنه ينشئ "callvirt" حتى في الحالات التي لا توجد فيها وظيفة افتراضية.لماذا يفعل ذلك؟

لقد راجعت ملاحظات تصميم اللغة التي لدي، وهي تشير بوضوح تام إلى أننا قررنا استخدام callvirt في 13/12/1999.ولسوء الحظ، فإنهم لا يفهمون مبرراتنا للقيام بذلك، لذلك سأضطر إلى الابتعاد عن ذاكرتي.

لقد حصلنا على تقرير من شخص ما (على الأرجح إحدى مجموعات .NET التي تستخدم لغة C# (اعتقد أنها لم تكن تسمى C# بعد في ذلك الوقت)) الذي كتب كودًا يستدعي طريقة على مؤشر فارغ، لكنهم لم يحصلوا على تقرير استثناء لأن الطريقة لم تصل إلى أي حقول (على سبيل المثال، "هذا" كان خاليًا، ولكن لم يستخدمه أي شيء في الطريقة).ثم تسمى هذه الطريقة طريقة أخرى استخدمت هذه النقطة وألقت استثناءً، وأعقب ذلك القليل من الحيرة.وبعد أن اكتشفوا ذلك، أرسلوا لنا ملاحظة حول هذا الموضوع.

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

ونتيجة ل(perhaps-) اهتمام جانبا ... GetType() أمر غير معتاد في أنه لا virtual - وهذا يؤدي إلى بعض <لأ href = "https://stackoverflow.com/questions / 194484 / ماذا يكون رأس أغرب-الزاوية حالة كنت قد مشاهدتها في ج-أو-صافي / 194671 # 194671 "> جدا، أشياء غريبة جدا .

(وضعت يكي كما هو إلى حد ما خارج الموضوع على السؤال الفعلي)

والمترجم لا يعرف نوع حقيقي من o في التعبير الأول، ولكنه لا تعرف نوع حقيقي في التعبير الثاني. يبدو انها تبحث فقط في عبارة واحدة في وقت واحد.

وهذا على ما يرام، لأن C # يعتمد بشكل كبير على JIT من أجل التحسين. فمن المحتمل جدا في مثل هذه الحالة البسيطة التي على حد سواء المكالمات ستصبح يدعو سبيل المثال في وقت التشغيل.

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

وأود أن الخطر تخمين أنه لأن المتنازل الأولى إلى متغير، والتي يمكن أن تحتوي يحتمل مثيل downcasted من نوع آخر قد قمت GetType يتم تجاوز (على الرغم من أننا يمكن أن نرى أنه لا)؛ والثاني لا يمكن أن يكون أي شيء آخر غير Object.

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