Я могу избежать исключений в C#, продолжив выполнение кода?

Я рекомендовал бы Вам использовать valgrind для Linux. Это поймает не освобожденную память среди других ошибок как запись в освобожденную память. Другая опция является брызговиком, который говорит Вам о не освобожденная память также. Используйте -fmudflap -lmudflap опции с gcc, затем запустите свою программу с MUDFLAP_OPTIONS=-print-leaks ./my_program.

Вот некоторый очень простой код. Это не подходит для сложного отслеживания, но предназначенное, чтобы показать Вам, как Вы сделали бы это в принципе, если бы необходимо было реализовать его сами. Что-то вроде этого (не учел материал, назвав зарегистрированный new_handler и другие детали).

template<typename T>
struct track_alloc : std::allocator<T> {
    typedef typename std::allocator<T>::pointer pointer;
    typedef typename std::allocator<T>::size_type size_type;

    template<typename U>
    struct rebind {
        typedef track_alloc<U> other;
    };

    track_alloc() {}

    template<typename U>
    track_alloc(track_alloc<U> const& u)
        :std::allocator<T>(u) {}

    pointer allocate(size_type size, 
                     std::allocator<void>::const_pointer = 0) {
        void * p = std::malloc(size * sizeof(T));
        if(p == 0) {
            throw std::bad_alloc();
        }
        return static_cast<pointer>(p);
    }

    void deallocate(pointer p, size_type) {
        std::free(p);
    }
};

typedef std::map< void*, std::size_t, std::less<void*>, 
                  track_alloc< std::pair<void* const, std::size_t> > > track_type;

struct track_printer {
    track_type * track;
    track_printer(track_type * track):track(track) {}
    ~track_printer() {
        track_type::const_iterator it = track->begin();
        while(it != track->end()) {
            std::cerr << "TRACK: leaked at " << it->first << ", "
                      << it->second << " bytes\n";
            ++it;
        }
    }
};

track_type * get_map() {
    // don't use normal new to avoid infinite recursion.
    static track_type * track = new (std::malloc(sizeof *track)) 
        track_type;
    static track_printer printer(track);
    return track;
}

void * operator new(std::size_t size) throw(std::bad_alloc) {
    // we are required to return non-null
    void * mem = std::malloc(size == 0 ? 1 : size);
    if(mem == 0) {
        throw std::bad_alloc();
    }
    (*get_map())[mem] = size;
    return mem;
}

void operator delete(void * mem) throw() {
    if(get_map()->erase(mem) == 0) {
        // this indicates a serious bug
        std::cerr << "bug: memory at " 
                  << mem << " wasn't allocated by us\n";
    }
    std::free(mem);
}

int main() {
    std::string *s = new std::string;
        // will print something like: TRACK: leaked at 0x9564008, 4 bytes
}

Мы должны использовать наше собственное средство выделения для нашей карты, потому что стандартный будет использовать наш переопределенный новый оператор, который привел бы к бесконечной рекурсии.

Удостоверяются, переопределяете ли Вы новый оператор, Вы используете карту для регистрации выделений. Удаление памяти, выделенной формами размещения новых, будет использовать тот оператор delete также, таким образом, это сможет стать хитрым, если некоторый код, который Вы не знаете, перегрузил оператор, новый не использование Вашей карты, потому что оператор удаляет, скажет Вам, что это не было выделено, и используйте std::free для освобождения памяти.

Также примечание, как Мир указанный для его решения также, это только покажет утечки, которые вызываются кодом с помощью нашего собственного определенного оператора, new/delete. Таким образом, если Вы хотите использовать их, поместить их объявление в заголовок и включать его во все файлы, за которыми нужно наблюдать.

7
задан John Rudy 13 October 2009 в 15:18
поделиться

13 ответов

Вы можете создать метод SkipOnError следующим образом:

private SkipOnError(Action action)
{
    try 
    {
        action();
    }
    catch
    {
    }
}

Затем вы можете вызвать его так:

try
{ 
    SkipOnError(() => /*line1*/);
    line2;
    line3;
} catch {}

Изменить: Это должно упростить пропуск данного исключения:

private SkipOnError(Action action, Type exceptionToSkip)
{
    try 
    {
        action();
    }
    catch (Exception e)
    {
        if (e.GetType() != exceptionToSkip) throw;            
    }
}

ПРИМЕЧАНИЕ: Я на самом деле не предлагаю вам делать это - по крайней мере, не на регулярной основе, поскольку я сам считаю это довольно хакерским. Но он как бы демонстрирует некоторые функциональные возможности, которые мы теперь можем делать на C #, ура!

На самом деле я бы сделал следующее: Рефакторинг line1 в метод ( Extract Method ). Этот новый метод должен обрабатывать любые предсказуемые исключения (если они могут быть обработаны) и, таким образом, оставлять вызывающего в известном состоянии. Потому что иногда вы действительно хотите сделать line1 , за исключением того, что, может быть, это нормально, если произойдет ошибка ...

10
ответ дан 6 December 2019 в 04:55
поделиться

Если возможно обработать исключение и продолжить, вам следует локализовать блоки try / catch.

try
{
  line1;
}
catch (ExpectedException e)
{
  // Handle your exception, prepare data for the next lines
  // Could you have prevented the exception in the first place?
}

try
{
  line2;
  line3;
}
catch (AnotherExpectedException e)
{
  // Maybe this exception you can't continue from. Perhaps log it and throw;
}

Помните, что исключения являются исключительными. Если возникает исключение, что-то должно было пойти не так. В первую очередь вам следует попытаться предотвратить исключения.

0
ответ дан 6 December 2019 в 04:55
поделиться

Для этого вам потребуются реифицируемые продолжения. CLR и C # не поддерживают этого и, вероятно, никогда не будут этого делать. : (

1
ответ дан 6 December 2019 в 04:55
поделиться
try
{
    line1
    line2
    ...
}
catch (Exception ex)
{
    ...
}
finally
{
    if(line1ErrorOccured)
    {
        line2
        ...
    }
}

не указано слишком много раздумий

0
ответ дан 6 December 2019 в 04:55
поделиться

Как говорили другие, сначала убедитесь, что "игнорирование" исключение - это то, чем вы действительно хотите заниматься. Если это все еще так, и синтаксические накладные расходы всех try-catch слишком высоки, вы можете завершить это idom выполнения.

У меня нет времени набросать весь код прямо сейчас - но вам нужно написать метод, который принимает анонимный делегат и выполняет его в рамках try-catch (возможно, регистрируя любые исключения или фильтруя те, которые "в порядке"). Назовите этот метод чем-то вроде tryDo. Затем вы можете написать код примерно так:

tryDo( delegate(){ line1; } );
tryDo( delegate(){ line2; } );

Это все еще немного многословно, но вы не хотите делать это слишком простым. Этого достаточно, чтобы заставить вас задуматься, правильно ли это делать: -)

1
ответ дан 6 December 2019 в 04:55
поделиться

Как было предложено, в этом конкретном примере вам следует обернуть try catch вокруг строки1. Однако на будущее у вас должны быть только условия в вашем блоке try catch, которые вы хотите выполнить, только если нет исключений.

1
ответ дан 6 December 2019 в 04:55
поделиться

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

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

1
ответ дан 6 December 2019 в 04:55
поделиться

вы можете заключить только строку 1 в блок try catch.

2
ответ дан 6 December 2019 в 04:55
поделиться

Вы можете изолировать каждую строку в блоке try-catch, но мне кажется, что это просто вредные шумы. Если вы уверены , что строки после первой строки не вызывают исключений или должны выполняться независимо от исключения и не будут вызывать дополнительных ошибок, вы можете добавить блок finally после первого перехвата. Пример:

try{
  line 1;
}
catch(Exception exc){
  //Error handling logic here
}
finally{
  line 2;
  line 3;
  .
  .
  .
  line n;
}
3
ответ дан 6 December 2019 в 04:55
поделиться

Если есть код, который вы всегда хотите выполнять, даже если возникает исключение, даже если исключение либо не попадает в блок catch, либо блок catch повторно генерирует или генерирует новое исключение, оно должно быть в блоке "finally":

try
{
  do(something);
}
catch (InvalidArgumentException ex)
{
  onlyDealWithOneExceptionType();
}
finally
{
  always.runs(no, matter, what);
}
4
ответ дан 6 December 2019 в 04:55
поделиться

просто попробуйте уловить строку1

try
{
    try
    {
        line1
    }
    catch (Exception ex)
    {
       ...
    }

    line2
    ...
}
catch (Exception ex)
{
    ...
}

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

5
ответ дан 6 December 2019 в 04:55
поделиться

Исключения не следует игнорировать. :)
Они существуют и выбрасываются по какой-то причине: возникла исключительная ситуация, определенное условие не выполнено, и я не могу продолжить работу ...

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

16
ответ дан 6 December 2019 в 04:55
поделиться

Вы всегда можете сделать

try 
{
   line1
}
catch (Exception ex)
{
}
line2
...

Но нет ничего лучше ключевого слова 'retry'.

1
ответ дан 6 December 2019 в 04:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: