Вызов метода с использованием тернарного оператора

Играя с новыми концепциями, я наткнулся на Ternary Operator и его красоту. Поиграв с ним некоторое время, я решил проверить его пределы.

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

int a = 5;
int b = 10;
a == b ? doThis() : doThat() 

    private void doThis()
    {
        MessageBox.Show("Did this");
    }
    private void doThat()
    {
        MessageBox.Show("Did that");
    }

Эта строка дает мне две ошибки:

Error   1   Only assignment, call, increment, decrement, and new object expressions can be used as a statement
Error   2   Type of conditional expression cannot be determined because there is no implicit conversion between 'void' and 'void'

Я никогда не использовал Ternary Operator, чтобы решить, какой метод вызывать, и я не знаю, возможно ли это вообще. Мне просто нравится идея однострочного If Else Statement для вызова метода.

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

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


person Kyle Uithoven    schedule 30.03.2011    source источник
comment
Убедитесь, что функции doThis и doThat возвращают значение типа int.   -  person Chandu    schedule 30.03.2011
comment
Если вам действительно нужен однострочный If Else, просто напишите его как одну строку: if (condition) doThis(); else doThat(); Конечно, тернарный оператор (если бы он работал так, как вы думали) был бы короче, но краткость не всегда хороша.   -  person Jim Mischel    schedule 30.03.2011


Ответы (8)


Тернарный оператор используется для возврата значений, и эти значения должны быть присвоены. Предполагая, что методы doThis() и doThat() возвращают значения, простое присваивание решит вашу проблему.

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

int a = 5;
int b = 10;
(a == b ? (Action)doThis : doThat)();

Это возвращает делегат Action, который затем вызывается скобками. Это не типичный способ достижения этого.

person NerdFury    schedule 30.03.2011
comment
Действительно, не правильный способ делать что-то; но определенно самый правильный ответ на вопрос: ОП хотел использовать тернарный оператор для выбора метода для вызова, и это именно то, что делает ваш фрагмент;) - person Edurne Pascual; 30.03.2011
comment
Почему это неправильно? Другой постер зашел так далеко, что назвал его глупым. - person Morgoth; 31.10.2014
comment
это здорово с новым оператором тела выражения (=›) - person Chris Hayes; 11.02.2016

Тернарный оператор должен что-то возвращать. Типичное использование выглядит так:

int x = (a > b) ? a : b;

Если вы попробуете что-то вроде

a + b;

Компилятор будет жаловаться.

(a > b) ? a - b : b - a;

в основном является сокращением для «a - b» или «b - a», которые сами по себе не являются законными утверждениями.

person iluxa    schedule 30.03.2011
comment
+1. Если бы DoThis() и DoThat() что-то возвращали, вы могли бы использовать тернарный оператор для управления выполнением и присваивать результат некоторой локальной переменной, однако это не является целью тернарного оператора; другие разработчики сочли бы, что возвращаемое значение важнее для вас, чем побочные эффекты. Используйте базовый оператор if; ваши намерения останутся ясными (и вам не нужно возиться с сигнатурами ваших методов, просто чтобы быть умным). - person KeithS; 30.03.2011
comment
+1. Еще один ключевой момент заключается в том, что оба значения, которые могут быть возвращены тернарным оператором, должны иметь один и тот же тип или неявно преобразовываться в один и тот же тип. - person Harry Steinhilber; 31.03.2011

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

(something ? new Action(DoThis) : DoThat)();

Если методы принимают параметры, это усложнится.
Вы можете либо поместить лямбда-выражения в условное выражение, либо использовать Action<T>.

Однако это очень глупый поступок.

person SLaks    schedule 30.03.2011

Вы должны быть в состоянии сделать это, хотя:

        int a = 5;
        int b = 10;

        var func = a == b ? (Action)doThis : (Action)doThat; // decide which method

        func(); // call it

Хотя не то, чтобы это было так уж полезно.

person skarmats    schedule 30.03.2011
comment
Для меня имеет больше смысла иметь (Action) перед каждой функцией, но в других примерах это было непосредственно перед doThis. Оба правильны? - person s3c; 15.01.2020

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

Поиграв еще немного, я понял, что вы МОЖЕТЕ использовать этот оператор для выполнения вышеуказанного оператора, но это приводит к плохому коду.

Если бы я изменил приведенное выше утверждение на;

int a = 5;
int b = 10;
int result = a == b ? doThis() : doThat(); 

private int doThis()
{
    MessageBox.Show("Did this");
    return 0;
}
private int doThat()
{
    MessageBox.Show("Did that");
    return 1;
}

Этот код будет компилироваться и выполняться так, как должен. Однако, если эти методы изначально не предназначались для возврата чего-либо и ссылались на другие области кода, теперь вам придется каждый раз обрабатывать возвращаемый объект для вызова этих методов.

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

int result = a == b ? doThis() : doThat();

if (result == 0)
   MessageBox.Show("You called doThis()!");

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

Теперь, когда я знаю, что вы можете эффективно возвращать любой тип в этих методах, это может стать немного более полезным. Это может считаться «Плохой практикой кодирования», но может оказаться очень полезным в ситуациях, для которых это никогда не ПРЕДНАЗНАЧАЛОСЬ.

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

UserPrivileges result = user.Group == Group.Admin ? GiveAdminPrivileges() : GiveUserPrivileges();

private UserPrivileges GiveAdminPrivileges()
{
      //Enter code here
      return var;
}
private UserPrivileges GiveUserPrivileges()
{
      //Enter code here
      return var;
}

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

person Kyle Uithoven    schedule 30.03.2011
comment
Согласен, но для полноты вы также можете вернуть логическое значение, строку или любой другой объект. Но на самом деле, что означает утверждение 1; действительно означает для компилятора? Не так много, и это не помогает в изучении хороших методов кодирования. - person Michael; 30.03.2011
comment
@ Майкл Я этого не знал, и в этом суть моего вопроса. Я хотел знать, как заставить это работать и преимущества/последствия его использования. Возможность выбора типа возврата делает его еще более полезным. - person Kyle Uithoven; 30.03.2011

Условный оператор — это выражение, возвращающее значение.
Его нельзя использовать с функциями, возвращающими void.

Вместо этого вы должны использовать обычный if.

person SLaks    schedule 30.03.2011
comment
Однако, возможно, OP предполагал, что пользователи могут использовать условный оператор в синтаксисе стиля C/C++, где синтаксис позволит пользователю вызывать метод в условной операции. Но я предполагаю, что из того, что сказал ОП, они не основывали свое использование условного оператора на синтаксисе C/C++. - person Jake; 11.01.2014

.NET не (легко) поддерживает (читабельную версию) это по какой-то причине. Это очень дергано и делает ваш код трудночитаемым. Логические деревья должны быть относительно легкими для обхода. Если бы я пришел на работу и весь код, который они использовали для присваивания значений, вызова методов и т. д., я бы просто ушел.

person FreeAsInBeer    schedule 30.03.2011
comment
Тебе еще нужно задание. - person NerdFury; 30.03.2011
comment
Я думаю, что единственный способ сделать код трудным для чтения — это использовать не описательные имена переменных и магические числа (числа, которые, кажется, не имеют смысла). Если тернарный оператор используется правильно, он должен читаться так же просто, как If/Else. - person Kyle Uithoven; 30.03.2011
comment
Имена методов могут быть длинными и часто включать параметры. Я не хочу читать строку из 200 символов, чтобы выяснить, где начинается вызов одного метода и заканчивается другой. Несколько строк (по крайней мере для меня) обычно легче читать и почти никогда не требуют многострочных строк. Сравните с: (VicePresidentsAttendee.AnnualSalary == MoneyHelper.MonetizeInt(SalaryThreshold)) ? RaiseHelper.AddABillionDollarBonus(VicePresidentAttendee.PersonId) : RaiseHelper.RegisterPersonForYearlyRaise(SalaryThreshold, PersonId, SickDaysLastQuarter, TypingSpeedInGoalWordsPerMinute, LunarPhaseOnLastPiDay); - person FreeAsInBeer; 30.03.2011
comment
вздор? Вы убили Google Translator :-) (и Bing тоже) - person Steve; 30.07.2014

Способ 1. Использование действия

Action Action = true is true ? new Action(() => A()) : new Action(() => B());
Action.Invoke();

Способ 2. Использование расширений

Button Button = new Button(){
    IsEnabled = false
};
Button.Switch((x) => x.IsEnabled).Invoke(() => A(), () => B())

Переключатель расширения:

public static R Switch<T, R>(this T sender, Func<T, R> method){
    return method.Invoke(sender);
}

Вызов расширения:

public static void Invoke(this bool condition, Action @true, Action @false){
    Action Action = condition ? @true : @false;
    Action.Invoke();
}

Способ 3. Использование расширений и условного оператора NULL в C# 6.0

Button.Case((x) => 0 > 1)?.With(() => A());
Button.Case((x) => 0 < 1)?.With(() => B());

Расширение с:

public static T With<T>(this T sender, Action method){
    method.Invoke();
    return sender;
}

Случай расширения:

public static object Case<T>(this T sender, Func<T, bool> method){
    return method.Invoke(sender) ? Convert.ChangeType(sender, typeof(T)) : null;
}
person ynsbl.eng    schedule 11.10.2020