متى يقوم عبارة عن عبارة عن حجتها، عندما يكون الهيكل؟

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

سؤال

لدي بعض الأسئلة حول التعليمات البرمجية التالية:

using System;

namespace ConsoleApplication2
{
    public struct Disposable : IDisposable
    {
        public void Dispose() { }
    }

    class Program
    {
        static void Main(string[] args)
        {
            using (Test()) { }
        }

        static Disposable Test()
        {
            return new Disposable();
        }
    }
}

أسئلتي هي:

  • هل سيتم استخدام البيان الذي يعمل على Disposable بنية، عاد من Test() مربع الهيكل، أم لا؟
  • كيف يمكنني العثور على الإجابة بنفسي؟

لمحاولة معرفة نفسي، فقد تفقد IL الناتجة عن التعليمات البرمجية أعلاه، وهنا il ل Main(...) طريقة:

.method private hidebysig static void Main(string[] args) cil managed
{
    .entrypoint
    .maxstack 1
    .locals init (
        [0] valuetype ConsoleApplication2.Disposable CS$3$0000)
    L_0000: call valuetype ConsoleApplication2.Disposable ConsoleApplication2.Program::Test()
    L_0005: stloc.0 
    L_0006: leave.s L_0016
    L_0008: ldloca.s CS$3$0000
    L_000a: constrained ConsoleApplication2.Disposable
    L_0010: callvirt instance void [mscorlib]System.IDisposable::Dispose()
    L_0015: endfinally 
    L_0016: ret 
    .try L_0006 to L_0008 finally handler L_0008 to L_0016
}

أظن أن الدعوة إلى الطريقة الافتراضية هناك، على L_0010 سوف نقدم عملية الملاكمة، ولكن الفعلية box التعليمات ليست هنا.

السبب في أنني أسأل هو أنه منذ فترة، ربما 1-2 سنوات، رأيت على الإنترنت "تحسين" من البيان المعتاد، علق شخص ما على. كانت القضية حيث تم استخدام عبارة الاستخدام كمصدتك كمصدتك لقفل قصير الأمد على كائن، حيث تم الحصول على القفل في الأسلوب، وتم إرجاع الصياغة، والتي عند التخلص منها، ستفرز القفل، رمز مثل هذا في

using (LockTheObject())
{
    // use the object
}

وكان التعليق أنه عن طريق تغيير نوع العودة من LockTheObject طريقة من IDisposable إلى الهيكل الفعلي المستخدم، تم تجنب الملاكمة.

لكنني أتساءل عما إذا كان هذا صحيحا، أو لا يزال صحيحا.

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

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

المحلول

يبدو كما لو أن أي نوع قيمة يتم وضعه في using البيان لن يكون محاصر. يبدو أن هذا هو التحسين C # كما يتم حذف الملاكمة فقط عند نوع القيمة التي تنفذ IDisposable هو في using بيان، ليس في أي سياق آخر.

لمزيد من المعلومات يرجى الاطلاع استخدام عبارة وأنواع القيمة المتاح:

منذ فترة كتب Ian Griffiths عن تحسن في فئة TimeDlock التي غير بها من فئة إلى بنية. أدى هذا التغيير إلى نوع القيمة التي تنفذ ناهتة. كان لدي سؤال مزعج في الجزء الخلفي من ذهني في الوقت الذي نسيته بسرعة. السؤال هو أن لا تحاصر حالات ذلك النوع عند الاتصال بالتخلص؟

و أيضا أوه لا! ليس the timedlock مرة أخرى!:

يشير John Sands إلى خلل في الرمز الذي أظهرته في مدونة حديثة لاستخدام مهلة على الأقفال دون التخلي عن معظم الراحة في C # lock الكلمة الرئيسية.

نصائح أخرى

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

تحديث: كان هذا السؤال موضوع مدونتي في مارس 2011. وبعد شكرا على السؤال العظيم!

إجابة أندرو هير صحيحة؛ أردت فقط إضافة ملاحظة اضافية مثيرة للاهتمام. التحسين الذي ننبعه - من استخدام Candvirt المقيدة لتخطي الملاكمة عند الإمكان - هو في الواقع يتحدث بدقة انتهاك لمواصفات C #. تنص المواصفات على أن كتلة أخيرا نحن نولد لمورد نوع القيمة هي:

     finally 
     {
         ((IDisposable)resource).Dispose();
     }

بوضوح هو تحويل الملاكمة على نوع القيمة. من الممكن بناء سيناريوهات مفتونة فيها نقص الملاكمة في التنفيذ مرئيا.

(شكرا جزيلا على فلاديمير Reshetnikov للإشارة إلى هذا الانتهاك الموصي بالنسبة لي.)

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

Unboxed object:
-----------------------------------------
|              DATA                     |
-----------------------------------------
 ^ managed pointer to struct

Boxed object:
------------------------------------------------------------
| GC/Object header |              [Boxed] DATA             |
------------------------------------------------------------
                    ^ The 'unbox' opcode gives a managed pointer to the boxed data
 ^ A *reference* to any instance of a reference type or boxed object, points here

DATA هو نفسه في كل من هذه الحالات¹.

تتوقع أساليب المثال على نوع القيمة التوجيه إلى البيانات المدارة على وجه التحديد جدا ملاكمة الكائنات غير مطلوبة. كما ترى أعلاه، constrained يستخدم OpCode قبل المكالمة. يحكي وقت التشغيل أن ما يلي callvirt التعليمات تتلقى مؤشر مدار إلى ConsoleApplication2.Disposable برايت بدلا من مرجع الكائن الذي يتلقى عادة. عند القيام بذلك، يمكن ل JIT حل الحمل المغلوب Dispose() تنفذها من قبل الهيكل واتصل بها مباشرة دون ملاكمة الكائن. بدون ال constrained بادئة، تم تمرير الكائن إلى callvirt يجب أن تكون التعليمات مرجع كائن، لأن إجراء الدقة الديناميكي للمكالمات الظاهرية القياسية يعتمد على حقيقة أن رأس GC / كائن هو دائماً في الموقع المتوقع - ونعم، سيفبر هذا الملاكمة لأنواع القيمة.

¹ سنضي قدما وتجاهل Nullable<T> في الوقت الراهن.

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