Подтвердить что ты не робот

Разумный или нет: несколько тернарных операторов С# + бросать, если не имеет себе равных

Вы находите следующий код С# разборчивым?

private bool CanExecuteAdd(string parameter) {
    return
        this.Script == null ? false
        : parameter == "Step" ? true
        : parameter == "Element" ? this.ElementSelectedInLibrary != null && this.SelectedStep != null
        : parameter == "Choice" ? this.SelectedElement != null
        : parameter == "Jump" ? this.SelectedStep != null
        : parameter == "Conditional jump" ? false
        : false.Throw("Unknown Add parameter {0} in XAML.".F(parameter));
}

где Throw определяется как:

public static T Throw<T>(this T ignored, string message) {
    throw new Exception(message);
}

Я знаю, что не идиоматический С#. Однако, вы могли бы понять это с первого взгляда или второго взгляда? Или я слишком далеко зашел?

4b9b3361

Ответ 1

Я использовал такой код на Java в достаточной степени. Мне не нравится бит false.Throw, но наличие нескольких условных выражений, подобных этому (особенно отформатирован таким образом), на мой взгляд прекрасен.

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

Один из вариантов использования false.Throw здесь будет примерно таким:

bool? ret = this.Script == null ? false
    : parameter == "Step" ? true
    : parameter == "Element" ? this.ElementSelectedInLibrary != null && this.SelectedStep != null
    : parameter == "Choice" ? this.SelectedElement != null
    : parameter == "Jump" ? this.SelectedStep != null
    : parameter == "Conditional jump" ? false
    : null;

if (ret == null)
{
    throw new ArgumentException(
       string.Format("Unknown Add parameter {0} in XAML.", parameter);
}
return ret.Value;

EDIT: На самом деле, в этом случае я бы не использовал либо if/else, либо этот шаблон... Я бы использовал switch/case. Это может быть очень компактным, если вы хотите:

if (this.Script == null)
{
    return false;
}
switch (parameter)
{
    case "Step": return true;
    case "Element": return this.ElementSelectedInLibrary != null && this.SelectedStep != null;
    case "Choice": return this.SelectedElement != null;
    case "Jump": return this.SelectedStep != null;
    default: throw new ArgumentException(
        string.Format("Unknown Add parameter {0} in XAML.", parameter);
}

Ответ 2

Почему бы не использовать переключатель? Я думаю, что это более читаемо.

private bool CanExecuteAdd(string parameter) {
    if (Script == null)
        return false;

    switch (parameter) {
        case "Step":
            return true;
        case "Element":
            return ElementSelectedInLibrary != null && SelectedStep != null;
        case "Choice":
            return SelectedElement != null;
        case "Jump":
            return SelectedStep != null;
        case "Conditional jump":
            return false;
        default:
            throw new Exception(string.Format("Unknown Add parameter {0} in XAML.", parameter));
    }
}

Ответ 3

Мое правило: используйте выражения для вещей без побочных эффектов. Используйте утверждения для вещей с одним побочным эффектом и для потока управления.

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

Ответ 4

Я голосую за непонятный.

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

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

Ответ 5

Мне нравится условный оператор и часто его использую.

Этот пример немного запутан, потому что характер оператора не ясен из макета и использования.

По крайней мере, я хотел бы сделать выбор и альтернативы понятными с помощью этого форматирования:

choice
  ? true-case
  : false-case

Но если мы применим это к вашему коду, это выявит отсутствие ясности при использовании конструкции таким образом:

return
    this.Script == null 
                ? false 
                : parameter == "Step" 
                    ? true
                    : parameter == "Element" 
                        ? this.ElementSelectedInLibrary != null && this.SelectedStep != null
                        : parameter == "Choice" 
                            ? this.SelectedElement != null
                            : parameter == "Jump" 
                                ? this.SelectedStep != null
                                : parameter == "Conditional jump" 
                                        ? false
                                        : false.Throw("Unknown Add parameter {0} in XAML.".F(parameter));

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

Ответ 6

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

Предположим, что если/тогда было бы предпочтительнее.

Ответ 7

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

Ответ 8

Быть неидиоматичным означает, что вы заставляете читателя тратить время на размышления о том, означает или нет то, что они читают, означает то, что, по их мнению, оно означает.

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

Есть ли какая-либо причина не использовать переключатель или else, если здесь построить?

Ответ 9

Почему бы не использовать nullool типа bool?

private bool? CanExecuteAdd(string parameter) {
return
    this.Script == null ? false
    : parameter == "Step" ? true
    : parameter == "Element" ? this.ElementSelectedInLibrary != null && this.SelectedStep != null
    : parameter == "Choice" ? this.SelectedElement != null
    : parameter == "Jump" ? this.SelectedStep != null
    : parameter == "Conditional jump" ? false
    : null;

}

Ответ 10

Сначала я был в ужасе, но на самом деле я не могу придумать, как написать это намного яснее в С# - я пытался подумать о чем-то, где у вас был массив Funcs, сопоставленный с результатами, но он стал еще более уродливым,

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

Ответ 11

Слишком сложно читать, сначала заботиться об исключениях.

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

Это один из немногих раз, это много отдельных возвратов в методе было бы приемлемо

private bool CanExecuteAdd(string parameter) 
{       
    if (this.Script == null)
        return false;

    if (parameter.NotIn([] {"Step", "Element", "Choice", "Jump", "Conditional jump"})
        throw new Exception("Unknown Add parameter {0} in XAML.".F(parameter));

    if (parameter == "Step") 
        return true;

    if (parameter == "Element")
        this.ElementSelectedInLibrary != null && this.SelectedStep != null;

        // etc, etc
}

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

public static bool In<T>(this T obj, IEnumerable<T> arr)
{
    return arr.Contains(obj);
}

Ответ 12

Мне все хорошо, но я изменил бы метод Throw:

static TObject Throw<TObject, TException>(this TObject ignored, TException exception)
{
   throw exception;
}

Это позволяет указать тип выброшенного исключения.

Ответ 13

К сожалению, тернарный оператор (?:) не является общей идиомой на языках C - я встречал много разработчиков C, С++ и С#, которым приходится делать паузу, чтобы прочитать их, потому что они не знакомы с ним или не используйте его. Это не делает его плохой языковой функцией или неразборчивым, однако те же самые разработчики могут называть пример OP неразборчивым, потому что он содержит языковые функции, которым им неудобно.

Я не нахожу пример неразборчивым - я много раз видел вложенных троичных операторов. Тем не менее, я чувствую, что использование переключателя было бы предпочтительным выбором для проверки "параметра" на строки.

Мне больше нравится, что метод расширения Throw игнорирует параметр 'this'. Что означало бы 42.Throw(...)? Если бы я просматривал код, я бы назвал его плохим дизайном.

Ответ 14

В C и С++ описанное использование является идиоматическим и причиной для оператора. Преимущество тернарного условного и цепного if-then-else состоит в том, что это выражение с известным типом. В С++ вы можете написать

foo_t foo = bar_p ? bar
          : qux_p ? qux
          : woz_p ? woz
          : (throw SomeExpression(), foo_t())
          ;

Обратите внимание на оператор запятой, который возвращает foo_t, который никогда не будет создан из-за броска.

Ответ 15

На самом деле я никогда не видел, чтобы троичный оператор вытолкнул до сих пор. Однако я понял, куда вы направляетесь. Кроме того, я согласен с Джоном, мне не нравится фальшивая часть.