как сократить синтаксис вызова функции, только если аргументы не равны нулю в С#?

скажем, у меня есть следующий код:

var arg = getArg();
if(arg != null)
{
  foo(arg);
}

есть ли способ сократить вызов нулевой проверки + функции? например, есть ли такой оператор, как, возможно, «безопасный», который будет работать как

foo(safe arg);

это переводится как «вызывать foo только в том случае, если 'arg' не равен нулю»? В основном я хочу не отправлять аргументы, которые могут быть нулевыми, функциям, а делать это коротким путем.

Спасибо!


person felisimo    schedule 01.05.2017    source источник
comment
Есть ли у вас доступ к коду реализации foo и право его изменять?   -  person CodeNotFound    schedule 01.05.2017
comment
Чтобы ответить на ваш вопрос, нет, вы не можете сократить его так, как вы спрашиваете. Однако функция (или, скорее, метод, поскольку это C#), если она спроектирована должным образом, не должна заботиться о том, передана ли она null в качестве параметра. Например, ваш метод foo должен иметь что-то наверху по строкам if (arg == null) return;, если он не должен работать, когда ему передается нулевое значение.   -  person Abion47    schedule 01.05.2017
comment
Скажем, я не хочу или не могу изменить foo(), поэтому добавление нулевой проверки внутри foo нежелательно, а скорее я хочу избежать вызова функции все вместе, поскольку я знаю, что это будет бесполезно с нулевым аргументом.   -  person felisimo    schedule 01.05.2017
comment
Самое близкое к тому, что вы описываете, это то, что вы можете использовать оператор с нулевым условием ?, если вы вызываете foo on arg. т. е. если вы сделали arg?.foo(), то foo будет вызываться только в том случае, если arg не равно нулю.   -  person Abion47    schedule 01.05.2017
comment
В качестве альтернативы вы можете использовать третичный оператор в разделе параметров метода для передачи значения по умолчанию, если arg равно нулю. например foo(arg == null ? new ArgObject() : arg);   -  person Abion47    schedule 01.05.2017
comment
@ Abion47 Понятно. Как вы думаете, стоит ли иметь эту функцию? Я нахожу ввод функции (которую я, возможно, не написал) и ее изменение довольно утомительным, не так ли?   -  person felisimo    schedule 01.05.2017
comment
Это признак серьезного запаха кода. Что-то не так с getArg(), он почти наверняка должен генерировать исключение. Что ж, еще не поздно это исправить, выкинуть исключение после его вызова. Fwiw, никогда не пытайтесь скрыть серьезную проблему дизайна, это должно причинить боль и заставить любого, кто это читает, захотеть исправить это.   -  person Hans Passant    schedule 01.05.2017
comment
Тернарный оператор @ Abion47 по-прежнему нуждается в проверке на нуль и задан вопрос об удалении проверки. Кроме того, ваше первое предложение полностью игнорирует дизайн и семантику класса.   -  person DesertFox    schedule 01.05.2017
comment
@ Abion47: функция (или, скорее, метод, поскольку это C #), если она спроектирована правильно, не должна заботиться о том, передана ли она null в качестве параметра - не согласен. Передача ссылки, отличной от null, вполне может быть предпосылкой для того, чтобы вызов имел смысл. В качестве метода void он может проверять свой аргумент и немедленно возвращать значение, если аргумент равен null, но методы, отличные от void, не обязательно могут это делать (поскольку значение по умолчанию не обязательно имеет смысл). Вместо этого в любом случае следует выбросить ArgumentNullException, и в этот момент, очевидно, ответственность за вызов кода...   -  person O. R. Mapper    schedule 01.05.2017
comment
... чтобы убедиться, что он не проходит null.   -  person O. R. Mapper    schedule 01.05.2017
comment
@DesertFox: в отсутствие дополнительного контекста я не могу сказать, что код, подобный приведенному в примере, неправилен сам по себе. Но я согласен с Гансом, что запах есть. То есть хорошо спроектированный API обычно не возвращает null значений. Я знаю, что существует множество примеров методов, которые возвращают null для указания некоторого состояния, но обычно это должны быть исключительные случаи, и с ними нужно обращаться, выбрасывая исключение. Вот почему я считаю, что этот тип сценария действительно не должен появляться в первую очередь. Да, Ганс предполагает; но мы все должны, учитывая отсутствие контекста, и его заметка разумна.   -  person Peter Duniho    schedule 01.05.2017
comment
@DesertFox: вопрос был об устранении необходимости проверки null -- когда методам не разрешено возвращать null как часть их обычной работы, вам не нужно проверять результат для null прежде чем передать его чему-то другому, таким образом, удаляя необходимость в нулевых проверках. Например, мне очень нравится в C# и C++ то, что new всегда возвращает значения, отличные от null; единственный способ, которым это невозможно, - это исключение.   -  person Peter Duniho    schedule 01.05.2017
comment
@O.R.Mapper Мой комментарий о методе, немедленно возвращающем значение null, был просто примером (отсюда и For instance). Я хочу сказать, что функция уже должна быть спроектирована таким образом, чтобы получать нулевое значение в качестве параметра и иметь возможность вести себя соответствующим образом. Возврат или выдача исключения или что-то еще полностью зависит от контекста, но его способность правильно выполняться не должна зависеть от проверки, которую вызывающий код должен выполнить до вызова.   -  person Abion47    schedule 01.05.2017
comment
@DesertFox Вопрос просто задан для сокращения синтаксиса нулевой проверки, не обязательно для его полного удаления. И я продемонстрировал, что если OP не может или не будет изменять getArgs или foo, использование троичного кода в поле параметра является самым близким к удалению нулевой проверки полностью.   -  person Abion47    schedule 01.05.2017
comment
@ Abion47: Если он выдает исключение в случае аргумента null, разумна предварительная проверка, выполненная вызывающим кодом . Тот факт, что возникает исключение, означает, что не следует вызывать этот метод, если вам не нужно передать что-то не-null. А проверка по вызывающему коду означает Если у меня есть arg, то мне нужно вызвать на нем метод, а если у меня нет arg, то ничего не должно происходить.   -  person O. R. Mapper    schedule 01.05.2017
comment
@HansPassant: что-то не так с getArg(), он почти наверняка должен вызвать исключение. - откуда вы знаете, что ОП не имеет дело с необязательным аргументом, который может быть или не быть?   -  person O. R. Mapper    schedule 01.05.2017
comment
@ORMapper В этом случае, опять же, см. Мой второй или третий ответ, чтобы узнать, как сократить чек. Я остаюсь при своем мнении, что во многих случаях проверка может даже не понадобиться в зависимости от того, как закодирован метод, а для случаев, когда она неизбежна, нет синтаксического сахара, чтобы исключить ее полностью, и есть только несколько способов избежать необходимости вводить все это.   -  person Abion47    schedule 01.05.2017


Ответы (2)


Лично я думаю, что «не звонить, если это значение равно null» должно быть достаточно редким явлением, поэтому ввод if не должен быть большой проблемой. Это сказало

Если вы хотите настроить некоторые методы расширения для обработки различных подсчетов списка параметров, вы можете сделать что-то вроде этого:

static class Extensions
{
    public static void IfNotNull<T>(Action<T> func, T arg)
        where T : class
    {
        if (arg != null)
        {
            func(arg);
        }
    }
}

то вы можете сделать это:

var arg = getArg();

Extensions.IfNotNull(foo, arg);

К сожалению, компилятор не выведет тип делегата для выполнения вышеуказанного, используя синтаксис метода расширения, что еще больше укоротит синтаксис. Но вы можете использовать функцию using static ..., чтобы ввести метод Extensions в область видимости, чтобы вы могли вызывать его без имени класса, например:

IfNotNull(foo, arg);

При необходимости распространите эту идею на другие сигнатуры функций. Вы просто отразите шаблоны, используемые для типов делегатов Action и Func.

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

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

void SafeFoo(ArgType arg)
{
    if (arg != null)
    {
        foo(arg);
    }
}

Мне это кажется настоящей болью, особенно если у вас не так много мест, которые вы хотели бы избежать if. Но это там.

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

person Peter Duniho    schedule 01.05.2017

Краткий ответ: НЕТ, НЕТ

Предложение:

Без сложного кода или включения сторонних библиотек в смесь, я думаю, вы должны учитывать следующие моменты при разработке своего кода:

  • Что должен делать этот код для предоставления диапазона аргументов?
  • Кто бы ни вызывал этот фрагмент кода, знает о возможных результатах указанного метода?

Первое, что вы можете сделать, это задокументировать методы.

В вашем примере кажется, что getArg может вернуть null. Итак, хорошим подходом может быть:

/// <summary>
/// Describe your method here
/// </summary>
/// <returns>Describe what it returns</returns>
/// <remarks>May return NULL if (explain why)</remarks>
public TypeName getArg()
{
    //do your code here
}

Потрясающий. Итак, теперь тот, кто намеревается использовать метод getArg(), будет иметь хорошее описание того, что делает метод, и, эй, он может даже дать вам экземпляр null в ответе.

Вы можете сделать то же самое с foo(). Теперь не ясно, должен ли foo() работать или нет, если в параметре передается null. Но из вашего описания функциональности кажется, что это сломает приложение, если это так.

Если это так, вы, вероятно, захотите, чтобы foo() выдавало исключение.

Итак, хорошим подходом будет:

/// <summary>
/// Describe your method here
/// </summary>
/// <param name="args">Describe the argument here</param>
/// <exception cref="System.ArgumentNullException">When args is null</exception>
public void foo(TypeName args)
{
    if (args == null)
        throw new ArgumentNullException(nameof(args));
    //do your code here
}

Мы почти на месте.

Теперь кусок кода, который связывает все вместе...

Вы знаете из документации (вы) при условии, что:

  • args может быть null
  • foo() выдает исключение, если args равно null

И из документации вы знаете, что хотите избежать try catch, насколько это возможно, из-за производительности.

  • БАРАБАННАЯ ДРОБЬ *

Предоставленный вами код должен быть окончательным кодом :)

var args = getArgs();
if (args != null)
    foo(args);

Но опять же. Это просто использование стандартного кода .net.

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

person DesertFox    schedule 01.05.2017
comment
Ожидаемым ответом будет использование тернарного оператора, почему вы не включили это в свой ответ? - person Zimano; 01.05.2017
comment
нисколько. Вопрос был: вызывать foo только в том случае, если 'arg' не равен нулю? В основном я хочу не отправлять аргументы, которые могут быть нулевыми, функциям, а делать это коротким путем. - person DesertFox; 01.05.2017
comment
тернарный оператор не удалит проверку на нуль. - person DesertFox; 01.05.2017