سؤال

تشير إلى الكثير من الوثائق على الشبكة ، وخاصة على ذلك ، على سبيل المثال: ما هي الطريقة الصحيحة لإعادة إدخال استثناء في C#؟يجب أن يكون هناك فرق بين "رمي ه ؛" و "رمي".

لكن من : http://bartdesmet.net/blogs/bart/archive/2006/03/12/3815.aspx,

هذا الرمز:

using System;

class Ex
{
   public static void Main()
  {
  //
  // First test rethrowing the caught exception variable.
  //
  Console.WriteLine("First test");
  try
  {
     ThrowWithVariable();
  }
  catch (Exception ex)
  {
     Console.WriteLine(ex.StackTrace);
  }

  //
  // Second test performing a blind rethrow.
  //
  Console.WriteLine("Second test");
  try
  {
     ThrowWithoutVariable();
  }
  catch (Exception ex)
  {
     Console.WriteLine(ex.StackTrace);
  }
}

 private static void BadGuy()
 {
   //
   // Some nasty behavior.
  //
   throw new Exception();
 }

   private static void ThrowWithVariable()
 {
   try
   {
         BadGuy();
   }
  catch (Exception ex)
  {
     throw ex;
  }
}

   private static void ThrowWithoutVariable()
{
  try
  {
     BadGuy();
  }
  catch
  {
     throw;
  }
   }
}

يعطي النتيجة التالية:

$ /cygdrive/c/Windows/Microsoft.NET/Framework/v4.0.30319/csc.exe Test.cs
Microsoft (R) Visual C# 2010 Compiler version 4.0.30319.1
Copyright (C) Microsoft Corporation. All rights reserved.

$ ./Test.exe
First test
   at Ex.ThrowWithVariable()
   at Ex.Main()
Second test
   at Ex.ThrowWithoutVariable()
   at Ex.Main()

وهو في تناقض تام مع منشور المدونة.

يتم الحصول على نفس النوع من النتيجة مع الرمز من: http://crazorsharp.blogspot.com/2009/08/rethrowing-exception-without-resetting.html

السؤال الأصلي : ما الخطأ الذي افعله ؟

تحديث : نفس النتيجة مع .NET 3.5 / csc.exe 3.5.30729.4926

تلخيص : كانت جميع إجاباتك رائعة ، شكرًا مرة أخرى.

وبالتالي فإن السبب هو وضع فعال بسبب الارتعاش 64 بت.

اضطررت إلى اختيار إجابة واحدة فقط ، وهذا هو السبب في أنني اخترت لوكه إجابه :

  • لقد خمن مشكلة الإطالة وحقيقة أنه قد يكون مرتبطًا ببنية 64 بت ،

  • لقد وفر علم noinlining الذي هو أبسط طريقة لتجنب هذا السلوك.

ومع ذلك ، فإن هذه القضية ترفع الآن سؤالًا آخر: هل هذا السلوك يتوافق مع جميع مواصفات .NET: تلك CLR و C# لغة البرمجة؟

تحديث : يبدو هذا التحسين متوافقًا وفقًا لـ: رمي مقابل Rethrow: نفس النتيجة؟ (شكرًا 0xa3)

شكرا مقدما لمساعدتكم.

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

المحلول

لا يمكنني تكرار المشكلة-باستخدام .NET 3.5 (32 بت) يعطيني نفس النتائج الموضحة في مقالة بارت.

أظن أن .NET 4 مترجم/ارتعاش-أو ربما يكون المترجم/الارتعاش 64 بت إذا كان هذا يحدث تحت 3.5 أيضًا-يضم إلى BadGuy الطريقة في أساليب الاتصال. حاول إضافة ما يلي MethodImpl يعزو إلى BadGuy ومعرفة ما إذا كان هذا يحدث أي فرق:

[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
private static void BadGuy()
{
    //
    // Some nasty behavior.
    //
    throw new Exception();
}

نصائح أخرى

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

أظن أن ما يحدث هو أن المترجم المضمّن قد استبدل ببساطة مكالمة badguy () برمي new Exception(); لأن هذا هو البيان الوحيد في Badguy ().

إذا قمت بإيقاف تشغيل خيار "تحسين الرمز" في شاشة Project Properties -> ، فإن كل من إصدار الإصدار والتصحيح ينتج نفس النتيجة التي تظهر Badguy () في الجزء العلوي من تتبع المكدس.

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

لترى أن هذا مرتبط بالارتعاش ، يمكنك تزيين الأساليب باستخدام أ MethodImplAttribute ينسب:

[MethodImpl(MethodImplOptions.NoOptimization)]
private static void ThrowWithoutVariable()
{
    try
    {
        BadGuy();
    }
    catch
    {
        throw;
    }
}

لاحظ أن IL لا يزال مختلفًا عن ThrowWithoutVariable و ThrowWithVariable:

.method private hidebysig static void  ThrowWithVariable() cil managed
{
  // Code size       11 (0xb)
  .maxstack  1
  .locals init ([0] class [mscorlib]System.Exception ex)
  .try
  {
    IL_0000:  call       void Ex::BadGuy()
    IL_0005:  leave.s    IL_000a
  }  // end .try
  catch [mscorlib]System.Exception 
  {
    IL_0007:  stloc.0
    IL_0008:  ldloc.0
    IL_0009:  throw
  }  // end handler
  IL_000a:  ret
} // end of method Ex::ThrowWithVariable

.method private hidebysig static void  ThrowWithoutVariable() cil managed
{
  // Code size       11 (0xb)
  .maxstack  1
  .try
  {
    IL_0000:  call       void Ex::BadGuy()
    IL_0005:  leave.s    IL_000a
  }  // end .try
  catch [mscorlib]System.Object 
  {
    IL_0007:  pop
    IL_0008:  rethrow
  }  // end handler
  IL_000a:  ret
} // end of method Ex::ThrowWithoutVariable

تحديث للإجابة على سؤال متابعة ما إذا كان هذا متوافقًا مع مواصفات CLI

في الواقع ، إنها متوافقة ، وهي السماح لمجمول JIT بتمكين تحسينات مهمة. الملحق ص الدول في الصفحة 52 (التركيز من قبلي):

تقوم بعض تعليمات CIL بإجراء عمليات فحص ضمنية لوقت التشغيل والتي تضمن الذاكرة ونوع السلامة. في الأصل ، ضمنت CLI أن الاستثناءات كانت دقيقة, ، وهذا يعني أن حالة البرنامج تم الحفاظ عليها عندما تم طرح استثناء. ومع ذلك ، فإن فرض استثناءات دقيقة للشيكات الضمنية يجعل بعض التحسينات المهمة مستحيلة من الناحية العملية.يمكن للمبرمجين الآن الإعلان ، من خلال سمة مخصصة ، أن الطريقة "مريحة" ، والتي تقول إن الاستثناءات الناشئة عن عمليات فحص وقت التشغيل الضمنية لا يجب أن تكون دقيقة.

تحافظ الشيكات المريحة على التحقق (من خلال الحفاظ على الذاكرة ونوع السلامة) مع السماح بتحسينات إعادة ترتيب التعليمات. على وجه الخصوص ، فإنه يتيح التحسينات التالية:

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

استخدم بناء تصحيح وسترى الفرق بشكل أكثر وضوحًا. مع تصحيح تصحيح ، سيظهر الجولة الأولى الموقع مثل throw ex الخط والثاني كما نشأ من الدعوة الفعلية إلى BadGuy. من الواضح أن "المشكلة" هي الدعوة إلى Badguy - وليس خط الرمي السابقين وستطارد عدد أقل من الأشباح مع المباشر throw; بيان.

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

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

    //This terrible hack makes sure track trace is preserved if exception is re-thrown
    internal static Exception AppendStackTrace(Exception ex)
    {
        //Fool CLR into appending stack trace information when the exception is re-thrown
        var remoteStackTraceString = typeof(Exception).GetField("_remoteStackTraceString",
                                                                 BindingFlags.Instance |
                                                                 BindingFlags.NonPublic);
        if (remoteStackTraceString != null)
            remoteStackTraceString.SetValue(ex, ex.StackTrace + Environment.NewLine);

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