2015-11-24 2 views
12

У меня есть метод, как:Вход пойманы исключения из вне метода, в котором они были пойманы

public TResult DoSomethingWithLogging<TResult>(Func<TResult> someAction) 
{ 
    try 
    { 
     return someAction.Invoke(); 
    } 
    catch (Exception ex) 
    { 
     LogException(ex) 
     throw; 
    } 

Этот метод используется следующим образом:

var result = DoSomethingWithLogging(() => Foo()); 

Я также хочу, чтобы войти исключения, которые были пойман внутри Foo(). Я не могу использовать throw в catch внутри Foo.

Как я могу поймать такие исключения?

Пример:

public static string Foo() 
{ 
    try 
    { 
     return "Foo"; 
    } 
    catch (Exception) 
    { 
     // I have to log this exception too without adding anything to Foo 
     return "Exception caught";    
    }  
} 
+6

Можете ли вы объяснить, почему у вас есть эти требования? Они кажутся очень конкретными и эффективно калечат, как вы можете написать хороший код. Возможно, мы сможем найти лучшее решение, которое удовлетворяет фактическим требованиям без необходимости искажать код для его размещения? – Rob

+0

Это в приложении MVC. Он используется в контроллере как ведение импорта данных из файла csv.В случае неудачного импорта я должен хранить информацию о проблеме. Но теперь, когда возникает определенная проблема, я могу просто пропустить эту строку в файле. Но мне все же приходится регистрировать, почему некоторые строки пропущены. –

+0

Я все еще думаю, что вы идете по нему не так. Вы можете, например, выставить событие внутри foo, которое позволяет вам отреагировать на исключенные исключения. Однако со стороны, предполагая что-либо изнутри foo, плохой дизайн. – Batavia

ответ

17

Вы можете связать событие FirstChanceException. Вот ваш код изменен, чтобы продемонстрировать это:

using System; 
using System.Runtime.ExceptionServices; 

public class Program 
{ 
    public static void Main() 
    { 
     AppDomain.CurrentDomain.FirstChanceException += 
     (object source, FirstChanceExceptionEventArgs e) => 
     { 
     Console.WriteLine("FirstChanceException event raised in {0}: {1}", 
      AppDomain.CurrentDomain.FriendlyName, e.Exception.Message); 
     }; 
    Console.WriteLine("Hello World"); 
    Console.WriteLine(DoSomethingWithLogging(() => Foo())); 
    } 

    public static TResult DoSomethingWithLogging<TResult>(Func<TResult> someAction) 
    { 
    try 
    { 
     return someAction.Invoke(); 
    } 
    catch (Exception ex) 
    { 
     Console.WriteLine(ex.Message); 
     throw; 
    } 
    } 

    public static string Foo() 
    { 
    try 
    { 
     throw new Exception("This will be caught"); 
     return"Foo"; 
    } 
    catch (Exception) //I have to log this exception too without adding anything too Foo 
    { 
     return "Exception caught";  
    }  
    } 
} 

Как правило, я был бы очень осторожен в этом ничем, кроме отладки сценариев. Как только он пойман, это не должно рассматриваться как исключение по коду выше. (Конечно, ловить его в первую очередь могло быть логической ошибкой, поэтому это действительно имеет некоторое значение в сценариях отладки).

Существуют также осложнения в многопоточных корпусах. В приведенном выше коде показано, как работает FirstChanceException, но если вы подключили его до вызова и затем отсоединили после того, как он все равно будет запускаться из-за каких-либо исключений из других потоков. Фильтрация этих данных может быть сложной. Вероятно, я начну с рассмотрения взгляда на стек вызовов, но я не уверен, что это лучший способ.

2

Я думаю, что вы идете по этому пути неправильно. Это очень плохая идея принять что-либо о реализации foo. (Как JDM упоминалось в комментариях это код запах, и он получит вас в неприятности)

Один из способов перестроить свой подход заключается в использовании события

class FooClass { 

    public event SkippedWorkEventHandler SkippedWork; 

    public void Foo() { 
     try { 
     /* some code */ 
     } catch (Exception ex) { 
     if (SkippedWork != null) { 
      /* pass in the relevant data to eventargs */ 
      SkippedWork(this, EventArgs.Empty) 
     } 
     } 
    } 

    public void DoSomethingWithFoo() { 
    SkippedWork += new LogSkippedWorkEventHandler 

    try { 
     Foo() 
    } catch (Exception ex) { 
     /*handle uncaughed exceptions here */ 
    } 

    } 
} 

Причиной этого является то, что от внешнего I не гарантирует, что я использую try/catch, чтобы обнаружить, что происходит внутри foo. я могу переписать его на простой, если проверить и вернуть, если мой тест не удастся. Результат foo будет таким же, но теперь ваши исключения не регистрируются.

С помощью разоблачения события вы позволяете кому-либо из узида, который интересуется определенными вещами (пропущенная строка на основе вашего комментария), реагировать на нее. и вы заключили контракт, в котором говорится, что вы будете чтить/поднимать это событие.

+0

Идея Intersting, но я уверен, что исключение будет поймано, потому что это приложение MVC с фильтром OnException. А также я не хочу изменять Foo, потому что Foo не должен зависеть от метода-оболочки, это метод из бизнес-логики, который вызывается из MVC-контроллера. И еще один: у меня есть дюжина разных Foo –

+2

. Я хочу сказать, что вы создаете зависимость, если полагаетесь на FirstChangeException. А именно зависимость от Foo до FirstChangeException. Кроме того, любой, кто когда-либо захочет реорганизовать foo по какой-либо причине, будет очень тяжело выяснить, что какой-то код работает, пока исключение, похоже, полностью поймано и обработано – Batavia

+1

Кроме того, я бы сказал, что использование OP исключений для управления бизнес-логикой является серьезным запахом кода. Исключением является нечто вроде «файл, который вы пытались прочитать, не существует» или «эта строка не является int». Бизнес-логика, такая как «эта строка из CSV содержит значение, которое не является допустимым почтовым кодом», является ошибкой _business_, а не исключением. – JMD

Смежные вопросы