Есть ли в C # выражение "true or throw" или "non-null or throw"? - PullRequest
1 голос
/ 15 июня 2019

Учитывая этот рабочий код:

if (!this.TryParse()) throw new Exception();
// or
if (this.Parse() == null) throw new Exception();

Так как C # 7 знает бросить выражения с ?? и ?:, мне интересно, есть (или почему нет) тамэто способ написать это как выражение «true or throw» или «non-null or throw» в C #, аналогично тому, что допускают другие языки, такие как JavaScript или PHP.Вот что я попробовал:

// COMPILE ERROR:

this.TryParse() || throw new Exception();

this.Parse() ?? throw new Exception();

_ = this.TryParse() || throw new Exception();


// WORKS:

_ = this.TryParse() ? 0 : throw new Exception();

_ = this.Parse() ?? throw new Exception();

Особенно, почему _ = <obj> ?? <throw> работает, а _ = <bool> || <throw> нет?

Зачем мне это делать?Чистая читаемость: я бы хотел, чтобы методы, основной целью которых было DOING, а не TESTING, были четко видны в начале строки как операторы вызова, а не скрывали их в условии if.Возьмите PHP в отличие от:

mysql_connect(...)
    OR die();

Ответы [ 4 ]

2 голосов
/ 16 июня 2019

Очевидный ответ: «потому что они не указали это таким образом».Сводная информация о выражении броска может быть найдена здесь .Я думаю, мы можем только догадываться почему, но на самом деле я думаю, что это имеет смысл.Кажется, что все новые возможности языка соответствуют этой единственной цели: уметь писать код более функционально.Давайте рассмотрим ваш пример разбора:

var parsed = Parse() ?? throw new Exception();

Чтобы записать это без выражения броска, вам нужно либо дважды оценить Parse, либо дважды записать переменную parsed для одной цели.

if (Parse() == null) throw new Exception();
var parsed = Parse();

и

var parsed = Parse();
// variable `parsed` can be null at this point
if (parsed == null) throw new Exception();
// variable `parsed` cannot be null anymore, the type of the variable is essentially mutated

Таким образом, первые два примера просты: написание this.TryParse() || throw new Exception(); вместо if (!this.TryParse()) throw new Exception(); будет буквально одним и тем же во всех аспектах.Так зачем вводить новый синтаксис для той же концепции, для которой у нас уже есть такой?Помните принципы разработки языка Python:

There should be one-- and preferably only one --obvious way to do it.

Последний пример более сложный.Заявление var a_non_false = this.TryParse() || throw new Exception(); синтаксически очень похоже на var a_non_null = this.Parse() ?? throw new Exception();, однако я утверждаю, что есть большая разница.Переменная a_non_false по сути является просто true константой (как если бы она была ложной, мы уже сгенерировали исключение), поэтому мы могли бы просто заменить a_non_false на true в остальной части функции и получить то же поведение.

Что касается части _ =, я вижу, что это другая проблема.Интересно, почему они не запрещали использование переменных отбрасывания в таких простых назначениях.Я бы определенно не стал использовать их для замены классического управления потоком.

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

2 голосов
/ 15 июня 2019

Для этого сценария можно использовать метод расширения ...

static void Main(string[] args)
{
    var a = false;

    a.OrThrow();
}


}
public static class ThrowExtension {
    public static void OrThrow(this bool b)
    {
        if(!b)
            throw new Exception();
    }
}
1 голос
/ 15 июня 2019

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

if (!this.TryParse()) throw new Exception();
// or
if (this.Parse() == null) throw new Exception();

Я автоматически предполагаю, что программист был сбит с толку.Зачем?Потому что две вещи:

  1. Функция TryParse возвращает логическое значение и заполняет выходной параметр.Он используется, когда вы полностью в порядке, когда входное значение не разбирается, но не требует исключения. Пример: int.TryParse()

  2. Функция Parse предполагает, что ввод действителен, и возвращает допустимое ненулевое значение или выдает исключение.Создание нового исключения в этом случае является излишним и может привести к запутыванию фактической проблемы.Пример: int.Parse()

Учитывая, что пример может быть плохим, давайте рассмотрим пару альтернатив.Во-первых, вы можете написать метод расширения для этого:

public static AssertNotNull(this object item) {
  if (item == null)
    throw new ArgumentException(“Object was null.”);
}

Таким образом, у нас есть два альтернативных синтаксиса:

  1. Ваш синтаксис (_ = this.TryParse() ? 0 : throw new Exception();
  2. MyСинтаксис:
    var val = this.TryParse();
    val.AssertNotNull();

Что легче читать? Я недавно оставил комментарий к коллегиальной проверке, когда он использовал ваш синтаксис. В его случае он выполнял заданиена правой стороне троичного, если первая часть троичного не была нулевой. ПЛОХО.

Итог: Всегда следует писать код как можно более явно.

1 голос
/ 15 июня 2019

_ = <obj> ?? <throw> работает, потому что в случае, если объект null, вместо возвращаемого значения выдается исключение.Тип выражения в правой части ?? не имеет значения.

_ = <bool> || <throw> не работает, поскольку || может применяться только к двум логическим значениям.Выражение справа не является логическим.(Я бы сказал, это не определено.)

...