как да съкратя синтаксиса на извикване на функция само ако аргументите не са null в c#?

да кажем, че имам следния код:

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

има ли начин да се съкрати нулевата проверка + извикването на функция? например има ли оператор като може би „safe“, който би работил като

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 на arg. т.е. ако сте направили arg?.foo(), тогава foo ще бъде извикано само ако arg не е нула.   -  person Abion47    schedule 01.05.2017
comment
Алтернатива е, че можете да използвате третичен оператор в секцията с параметри на метода, за да предадете стойност по подразбиране, ако arg е null. напр. 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 преди да го прехвърлите на нещо друго, като по този начин премахнете необходимостта от нулеви проверки. Например, едно нещо, което наистина харесвам в C# срещу C++ е, че new винаги връща стойности, различни от null; единственият начин да не може е да бъде хвърлено изключение.   -  person Peter Duniho    schedule 01.05.2017
comment
@O.R.Mapper Коментарът ми за метода, който незабавно се връща при null, беше само пример (следователно например). Искам да кажа, че функцията вече трябва да е проектирана да получава нулева стойност като параметър и да може да се държи по съответния начин. Дали това е връщане или хвърляне на изключение или нещо друго зависи изцяло от контекста, но способността му да се изпълни правилно не трябва да зависи от проверка, която извикващият код трябва да направи преди повикването.   -  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(), почти сигурно трябва да хвърли изключение. - откъде знаеш, че OP не се занимава с незадължителен аргумент, който може или не може да бъде там?   -  person O. R. Mapper    schedule 01.05.2017
comment
@O.R.Mapper В този случай отново вижте втория или третия ми отговор за начини за съкращаване на проверката. Поддържам твърдението си, че в много случаи проверка може дори да не е необходима в зависимост от това как е кодиран методът, а за случаите, в които е неизбежна, няма синтактична захар, която да я елиминира напълно, а има само шепа начини, които избягват да се налага да изписвате всичко.   -  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' не е null? По принцип искам да не изпращам аргументи, които може да са нулеви, към функциите, но го правя по кратък начин. - person DesertFox; 01.05.2017
comment
троичен оператор не би премахнал проверката за нула. - person DesertFox; 01.05.2017