Генерировать и обрабатывать пользовательские сообщения внутри методов? - PullRequest
3 голосов
/ 05 августа 2011

Каков наилучший способ обработки функции, которая может иногда не выполнять оценку, но даже если она не работает, не требует остановки родительской процедуры, хотя иногда может потребоваться объяснение для пользователя?

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

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

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

Ответы [ 8 ]

3 голосов
/ 05 августа 2011

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

... гм. Время сойти с моей высокой лошади.

На полном серьезе, я не знаю природу проблемы, которую вы пытаетесь решить. Первый вопрос заключается в том, действительно ли сбой в вашем алгоритме является провалом, или невозможность оценки может быть выражена значением NaN или 0? Если это действительно принципиальная концептуальная ошибка, способен ли алгоритм проверить ввод перед продолжением? Если это так, выведите ArgumentException или производный от него класс - предпочтительно последний. Это означает, что любой другой потребляющий код может обрабатывать общий случай (например, в сценарии IoC), а ваш код может обрабатывать конкретный случай, о котором можно разумно ожидать, чтобы знать. Если вы сделаете это, я предлагаю, чтобы любая сборка, содержащая эту функцию, также обеспечивала некоторые статические функции проверки, которые позволяют вызывающей стороне проверять допустимость входящих аргументов перед вызовом чего-то, что может вызвать исключение.

1 голос
/ 28 января 2014

Вы можете обернуть нужную информацию классом общего результата.

public class MyClass
{
    public static void Main()
    {
        var result = new MyClass().TrySomeFunction();

        if (result.Succeeded)
        {
            // use it
            MyReturningResultType resultValue = result.GetResultValue();
        }
        else
        {
            // use message
            string message = result.ResultMessage;
        }

    }

    Result<MyReturningResultType> TrySomeFunction()
    {
        try
        {
            MyReturningResultType value = CalculateIt();
            return new Result<MyReturningResultType>(value) { Succeeded = true };
        }
        catch (Exception exception)
        {
            return new Result<MyReturningResultType>(default(MyReturningResultType))
            {
                Succeeded = false,
                ResultMessage = exception.Message
            };
        }
    }
}

public class Result<T>
{
    public Result(T resultValue) { this.ResultValue = resultValue; }
    public bool Succeeded { get; set; }
    public string ResultMessage { get; set; }
    public T GetResultValue()
    {
        if (ResultValue is T)
        {
            return (T)this.ResultValue;
        }

        return default(T);
    }

    private T ResultValue;
}
1 голос
/ 05 августа 2011

Я бы не выбрасывал исключения, кроме как в исключительных обстоятельствах. Исключения очень дорогие. Сохраните их для действительно плохих вещей, как упомянутое пропавшее поле. Вместо этого я бы возвратил объект, который имеет флаг успеха, обнуляемый результат и список сообщений, которые вызывающая подпрограмма может затем отобразить пользователю (или передать обратно на следующие уровни для отображения пользователю)

1 голос
/ 05 августа 2011

Вы можете передать out параметр "строка например" в функцию, поэтому, когда функция не работает, возвращая false или null, печатайте причину для пользователя. Точно так же, как Microsoft делает с TryParse .. но здесь мы также получаем причину сбоя:

public bool TrySomeFunction(out string errorMessage) 
{
    try
    {
        //code that may cause exception

        return true;//operation completed successfully
    }
    catch (Exception exception)
    {
        errorMessage = exception.Message;
    }

    return false; 
}
1 голос
/ 05 августа 2011

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

EDIT:

После перечитывания поста мне показалось, что я пропустил, что в коде нет классов UI. Как и в случае с комментариями, любые отображаемые пользователем сообщения должны быть оставлены на уровне пользовательского интерфейса для представления, а не из какого-либо другого кода. Поэтому вам нужно вернуть флаг сортировки обратно в пользовательский интерфейс, чтобы он мог отображать сообщение. Если этот экземпляр не является прерывателем функции, то я бы сказал, что исключение цепочки исключено. Я предполагаю, что это оставляет вам необходимость определять его в возвращаемом значении каждого вызова функции (как вы делаете в первом случае с возвратом NULL) - возможно, пользовательский тип данных для возвращаемых значений с флагом «Предупреждение» или что-то еще

0 голосов
/ 05 августа 2011

Я согласен с @Tom, что вы не должны бросать исключения, чтобы просто контролировать ход программы, так как они дорогие. Еще один подход, который вы могли бы рассмотреть, - передать делегата (это не в моей голове):

public static void Main()
{
    Action<string> errorTarget = delegate(string s) { Console.WriteLine(s); }; 

    SomeFunction(errorTarget);
}

private static void SomeFunction(Action<string> errorTarget)
{
    ...
    // Send non-fatal errors to the errorTarget
    if (result == null)
    {
        // Build the error message, then call errorTarget
        errorTarget(errorMessage);
    }
    ...
}
0 голосов
/ 05 августа 2011

для меня это звучит действительно об обработке исключений.

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

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

0 голосов
/ 05 августа 2011

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

Например, если у вас есть обязательное поле, которое пользователь не заполнил, выведите MissingRequiredFieldException со свойством MissingFieldName(s). Таким образом, в стеке вы можете вывести значимое сообщение пользователю о том, какие поля пропущены.

Лучшая часть этого подхода заключается в том, что он не использует функцию вычисления для создания читаемой пользователем строки. Это будет сделано выше, в соответствующем месте. Что происходит, когда вы должны интернационализировать? Собираетесь ли вы реорганизовать свою функцию переключения между языками в зависимости от пользователя? Звучит как основной случай смешивания проблем ... Что делает функция вычисления, обрабатывающая код пользовательского вывода?

...