Вопрос

Ссылаясь на много документации по сети, особенно на так, например: Какой правильный способ перебросить исключение в 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/reathrying-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-битный) дает мне одинаковые результаты, описанные в статье BART.

Я думаю, что компилятор .NET 4 / Jitter - или, возможно, это 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 ().

Если вы отключите опцию «Оптимизируйте код» в свойствах проекта -> Экран сборки, то сборки выпуска и отладки дают тот же результат, который показывает 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 включить важную оптимизацию. Приложение F. Государства на странице 52 (акцент меня):

Некоторые инструкции CIL выполняют неявные проверки выполнения, которые обеспечивают безопасность памяти и типа. Первоначально CLI гарантировано, что исключения были точными, что означает, что состояние программы сохранилось, когда исключение было брошено. Однако, применение точных исключений для неявных проверок делает некоторые важные оптимизации практически невозможными для применения.Программисты теперь могут объявить, через пользовательский атрибут, что метод «расслаблен», который говорит, что исключения, возникающие из неявных проверок выполнения, не должны быть точными.

Расслабленные проверки сохраняют проверку проверки (путем сохранения безопасности памяти и типа), позволяя оптимизациями, которые переупорядочить инструкции. В частности, он позволяет следующим оптимизациям:

  • Подъемное неявное управление просмотру века вне циклов.
  • Переупорядочение петли итерации (например, векторные и автоматические многопоточные)
  • Сердечные петли
  • Встроив, что делает включенный метод как минимум так же быстро, как эквивалентный макрос

Используйте отладочную сборку, и вы увидите разницу более четко. С отладочной сборкой первый прогон покажет местоположение как throw ex линия и второе, как возникать из фактического вызова BadGuy. Отказ Очевидно, что «проблема» - это призыв к Badguy - не бросить бычную линию, и вы будете преследовать меньшее количество призраков с прямым throw; утверждение.

В стога прослеживают это мелкие преимущества, которые не так, как немедленный очевидный, в очень глубоком стеке вы будете замаскировать фактический источник проблемы и ослабить некоторую точность путем вручную, выбрасывая исключение вместо того, чтобы использовать встроенные в реблоке.

На боковом заметке я нашел взлом, размещенный в блоге (я потерял ссылку), который позволяет сохранить вызовов на Rethrow. Это в основном полезно, если вы поймаете исключение в одном контексте (скажем, в потоке, выполняющей асинхронную операцию) и хотите обратиться в другое (скажем, в другом потоке, которая запустила асинхронную операцию). Он использует какие-либо недокументированные функциональные возможности, включенные, чтобы разрешить сохранение следов стека на границах удаления.

    //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