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

Как мне переписать очень сложное выражение if на С#?

В моем коде С# у меня есть оператор if, который начался невинно:

if((something == -1) && (somethingelse == -1) && (etc == -1)) {
    // ...
}

Он растет. Я думаю, что в нем должно быть 20 статей.

Как мне с этим справиться?

4b9b3361

Ответ 1

Используйте ворота, где это возможно.

оператор if

if(bailIfIEqualZero != 0 && 
   !string.IsNullOrEmpty(shouldNeverBeEmpty) &&
   betterNotBeNull != null &&
   !betterNotBeNull.RunAwayIfTrue &&
   //yadda

реорганизованная версия

if(bailIfIEqualZero == 0)
  return;

if(string.IsNullOrEmpty(shouldNeverBeEmpty))
  return;

if(betterNotBeNull == null || betterNotBeNull.RunAwayIfTrue)
  return;

//yadda

Ответ 2

Извлеките его в функцию и сделайте каждое условие защитным предложением:

int maybe_do_something(...) {
    if(something != -1)
        return 0;
    if(somethingelse != -1)
        return 0;
    if(etc != -1)
        return 0;
    do_something();
    return 1;
}

Ответ 3

Предполагая, что все эти условия действительно необходимы, вы можете объединить условия в один или несколько вызовов uber-booleans или функций для повышения удобочитаемости.

например.

bool TeamAIsGoForLaunch = BobSaysGo && BillSaysGo;
bool TeamBIsGoForLaunch = JillSaysGo && JackSaysGo;

if (TeamAIsGoForLaunch && TeamBIsGoForLaunch && TeamC.isGoForLaunch())

Ответ 4

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

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

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

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

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

Это, конечно, может не применяться в вашей ситуации, но часто такой сложный комплекс действительно означает, что либо недостаточно классов, разделяющих ответственность (другим решением нашей проблемы могли быть классы factory, которые содержали конкретные правила и переопределения), или функциональность должна управляться данными.

Ответ 5

Снова преобразуйте его в функцию.

bool Check()
{
  return (something == -1) && (somethingelse == -1) && (etc == -1);
}

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

Ответ 6

Есть много способов справиться с этим, но позвольте мне выбрать несколько.

Во-первых, существует случай, когда все критерии (все И-критерии в вашем if-statement) + код для выполнения, если все они истинны, являются разовой ситуацией.

В этом случае используйте код, который у вас есть. Возможно, вы захотите сделать то, что уже предложили некоторые другие, переписать для использования типа кода Guard-clause.

Другими словами, вместо этого:

if (a && b && c && d && ......)
    DoSomething();

... вы переписываете что-то похожее на это:

if (!a) return;
if (!b) return;
if (!c) return;
if (!d) return;
if (!...) return;
DoSomething();

Почему? Поскольку, как только вы начинаете вводить OR-критерии в микс, становится сложно прочитать код и выяснить, что произойдет. В приведенном выше коде вы разбиваете критерии для каждого AND-оператора (&), и, таким образом, код становится легче читать. В основном вы переписываете код, говоря "если это и то, или что-то другое, и что-то третье, или что-то другое, тогда сделайте что-то" быть ", если это произойдет, а затем выйдите, если это другая вещь, а затем выйдите, если некоторые другой, затем выйдите, если ничего из вышеперечисленного, не сделайте что-нибудь".

Однако во многих случаях у вас также есть случай повторного использования. Если некоторые из этих критериев появляются в другом месте, но код, который будет фактически выполняться (DoSomething), не то же самое, тогда я бы пошел, опять же, что уже предложили другие. Перепишите критерии в методы, возвращающие результат Boolean в зависимости от результата оценки критериев.

Например, что легче читать, это?

if (a && b && c && d && e && f && (h || i) && (j || k) || l)

или это:

if (CanAccessStream() && CanWriteToStream())

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

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

Третий вариант - это то, где критерии отличаются в нескольких местах в коде, но фактический исполняемый код тот же.

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

Например, вы можете написать это:

if (stream != null && buffer != null && inBuffer > 0 && stream.CanWrite)
  stream.Write(buffer, 0, inBuffer);
else
    throw new InvalidOperationException();

или вы могли бы написать это:

if (inBuffer > 0)
{
    Debug.Assert(buffer != null);
    WriteToStream(buffer, inBuffer);
}

...

private void WriteToStream(Byte[] buffer, Int32 count)
{
    if (stream.CanWrite)
        stream.Write(buffer, 0, count);
    else
        throw new InvalidOperationException();
}

Я бы сказал, что второй способ легче читать, и он более многоразовый, чем первый.

Ответ 7

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

if(something != -1)
    return MyEnum.Something;
if(somethingelse != -1)
    return MyEnum.SomethingElse;
if(etc != -1)
    return MyEnum.SomethingElseEntirely;
return MyEnum.None;

Ответ 8

Выполнять агрегированные операции над списком ваших значений.

if (new[] { something, somethingelse, ... }.All(x => x == -1)) {
}

* Редактировать: Givin 'данные дополнительные строки:

var Data = new[] { something, somethingelse, ... };
if (Data.All(x => x == -1)) {
}

Ответ 9

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

Ответ 10

Вы также можете разделить состояние на класс.

class mystate
{
    int something;
    int somethingelse;
    int etc;

    bool abletodostuff()
    {
        return (something == -1) && (somethingelse == -1) && (etc == -1);
    }
}

Ответ 12

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

Чтобы оценить выражения, вы можете использовать шаблон

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

Ответ 13

Не так часто бывает, что многие предложения в одном случае "если". Обычно вы обнаруживаете, что вам нужно вложить "ifs", чтобы получить нужную вам логику, когда вам нужно выполнить какую-то строку, независимо от правды некоторых условий. Я не говорю, что они гнездятся, если вам не нужно, если все они должны быть протестированы одновременно. Только если есть общая функциональность. Другое соображение - установить логическую переменную с результатом некоторого набора этих условий, что может облегчить ее понимание. Если ваши переменные являются массивом или коллекцией, можете ли вы их пропустить? Вы тестируете их все против -1?

Ответ 14

В то время как мне нравится решение Dario (как я прокомментировал, я бы поставил его в логическую функцию, поэтому мне не нужно было иметь новую часть условия if, если...) Я не уверен, что неправильно с:

if((something == -1) &&
   (somethingElse == -1) &&
   (elseElse == -1) &&
   ...
  )

Я думаю, что, наверное, гораздо легче читать, чем много ((A && B) || (C && (D || E))), с которыми мне приходится иметь дело...

Ответ 15

Я делаю это так, как мне нравится никто, кроме меня. Мне все равно, выглядит ли код "хорошо". Если у меня есть > 1 тест в этом условном, это означает, что я хочу получить еще больше, или я могу прокомментировать некоторые из них в или из, и я хочу упростить создание таких изменений.

Поэтому я кодирую его так:

if (true
  && test_1
  && test_2
  ...
  )
{
  ...
}

Это упрощает комментирование тестов или добавление новых, как редактирование в 1 строку.

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

Ответ 16

И что-то вроде этого

Я объясню чуть дальше. (И исправить глупые ошибки: S)

//Interface to be able to which classes are able to give a boolean result used in the if stuff
public interface IResultable
{
    bool Result();
}

//A list that is IResultable itself it gathers the results of the IResultables inside.
public class ComparatorList<T> : List<T>, IResultable where T : IResultable
{
    public bool Result()
    {
        bool retval = true;
        foreach (T t in this)
        {
            if (!t.Result())
            {
                retval = false;
            }
        }
        return retval;
    }
}

//Or two bools
public class OrComparator : IResultable
{
    bool left;
    bool right;

    public OrComparator(bool left, bool right)
    {
        this.left = left;
        this.right = right;
    }
    #region IResultable Members

    public bool Result()
    {
        return (left || right);
    }

    #endregion
}

// And two bools
public class AndComparator : IResultable
{
    bool left;
    bool right;

    public AndComparator(bool left, bool right)
    {
        this.left = left;
        this.right = right;
    }
    #region IResultable Members

    public bool Result()
    {
        return (left && right);
    }

    #endregion
}

// compare two ints
public class IntIsComparator : IResultable
{
    int left;
    int right;

    public IntIsComparator(int left, int right)
    {
        this.left = left;
        this.right = right;
    }
    #region IResultable Members

    public bool Result()
    {
        return (left == right);
    }

    #endregion
}

Есть ли у вас много там, если это утверждение может быть круто:)

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

//list of ands
ComparatorList<AndComparator> ands = new ComparatorList<AndComparator>();    
ands.Add(new AndComparator(true,true));

//list of ors
ComparatorList<OrComparator> ors = new ComparatorList<OrComparator>();
ors.Add(new OrComparator(false, true));

//list of intiss
ComparatorList<IntIsComparator> ints = new ComparatorList<IntIsComparator>();
ints.Add(new IntIsComparator(1, 1));

//list of all things :)
ComparatorList<IResultable> collected = new ComparatorList<IResultable>();
collected.Add(ands);
collected.Add(ors);
collected.Add(ints);

// if all things are as they must be :)
if (collected.Result())
{
    //Evertything is as it schould be :)
}

Ответ 17

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

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

Рефакторинг кода может помочь вам лучше понять его и как состояния связаны друг с другом. Я бы начал здесь сначала, если бы я был вами:)

Ответ 18

ты говоришь, что смотришь на строки, так как насчет чего-то подобного, о ком кто-то уже прокомментировал.

        var items = new List<string>();

        items.Add("string1");
        items.Add("string2");

        if (items.Contains("string2"))
        {
            // do something
        }

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