我应该如何在 C# 中重写一个非常大的复合 if 语句?

发布于 2024-07-24 06:36:59 字数 212 浏览 9 评论 0原文

在我的 C# 代码中,我有一个 if 语句,它的开头很天真:

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

它正在增长。 我想现在里面一定有20个条款。

我应该如何处理这个问题?

In my C# code, I have an if statement that started innocently enough:

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

It's growing. I think there must be 20 clauses in it now.

How should I be handling this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(18

伪装你 2024-07-31 06:37:00

虽然我喜欢达里奥的解决方案(正如我所评论的,我将它放在一个布尔函数中,这样我就不必在 if 的条件部分中有一个新的...)我不确定有什么问题:

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

我我认为这可能比我必须处理的许多 ((A && B) || (C &&(D||E))) 更容易阅读......

While I like Dario's solution (as I commented, I'd put it in a boolean function so I didn't have to have a new in the condition part of an if...) I'm not sure what's wrong with:

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

I think that's probably a lot easier to read than a lot of ((A && B) || (C &&(D||E))) that I have to deal with...

提笔落墨 2024-07-31 06:37:00

我这样做的方式除了我之外没有人喜欢。 我不在乎代码是否“看起来不错”。 如果我在该条件中获得了 >1 测试,则意味着我可能想要更多,或者我可能想对其中一些进行注释或注释,并且我想简化此类更改的制作。

所以我这样编码:

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

这使得注释掉测试或添加新测试变得很容易,作为 1 行编辑。

但我会第一个承认,它并不声称很漂亮。

I do this in a way nobody likes except me. I don't care if the code "looks good". If I've got >1 test in that conditional, that means I'm liable to want even more, or I may want to comment some of them in or out, and I want to simplify the making of such changes.

So I code it like this:

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

This makes it easy to comment out tests, or add new ones, as 1-line edits.

But I'll be the first to admit, it doesn't claim to be pretty.

一片旧的回忆 2024-07-31 06:37:00

如果你真的必须检查所有这些条件,你就无法摆脱它们。 你只能重构它并使其更具可读性,但逻辑保持不变。

我很惊讶没有人提到任何有关重新设计代码的事情。 你真的需要 20 个不同的状态吗? 根据我的经验,许多状态通常依赖于其他状态,因此在逻辑上通常是多余的。

重构代码可能会帮助您更好地理解代码以及状态如何相互耦合。 如果我是你,我会先从这里开始:)

If you really have to check against all these conditions, you can't get rid of them. You can only refactor it and make it more readable, but the logic stays the same.

I'm quite surprised that no one has mentioned anything about redesigning your code. Do you really really need 20 differents states? In my experience, a lot of states are often depending on other states and are therefore often logically redundant to check against.

Refactoring your code might help you get a better understanding of it and how the states are coupled to each other. I'd start here first if I were you :)

此刻的回忆 2024-07-31 06:37:00

像这样的事情

我会进一步解释一下。 (并修复愚蠢的错误: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
}

您是否有很多 if 语句,这可能很酷:)

所以我们有这个结构来处理大量比较。 我将给出一个小实现示例。

//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 :)
}

And Something like this

I'll explain a litlle further. (And fix the stupid errors :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
}

Is you have a lot of there if statements this could be cool :)

So we have this sturcture for handeling a lot of comparisons. I'll give a little example implementation.

//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 :)
}
我做我的改变 2024-07-31 06:37:00

你说你正在查看字符串,那么有人已经评论过这样的东西怎么样?

        var items = new List<string>();

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

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

您甚至可以从某种配置文件中获取值来填充列表。

you say you are looking at strings so how about something like this which someone commented about already.

        var items = new List<string>();

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

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

you could even grab the values from a configuration file of some sort to populate the list.

你曾走过我的故事 2024-07-31 06:36:59

尽可能使用门。

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

Use gates where possible.

the if statement

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

the refactored version

if(bailIfIEqualZero == 0)
  return;

if(string.IsNullOrEmpty(shouldNeverBeEmpty))
  return;

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

//yadda
瀟灑尐姊 2024-07-31 06:36:59

将其分解为一个函数,并使每个条件成为一个保护子句:

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

Factor it out into a function and make each condition a guard clause:

int maybe_do_something(...) {
    if(something != -1)
        return 0;
    if(somethingelse != -1)
        return 0;
    if(etc != -1)
        return 0;
    do_something();
    return 1;
}
宫墨修音 2024-07-31 06:36:59

假设所有这些条件确实必要,您可以将这些条件合并到一个或多个超级布尔值或函数调用中以增强可读性。

例如,

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

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

Assuming all those conditions are really necessary, you could consolidate the conditions into one or more uber-booleans or function calls to enhance readability.

E.g.,

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

if (TeamAIsGoForLaunch && TeamBIsGoForLaunch && TeamC.isGoForLaunch())
不奢求什么 2024-07-31 06:36:59

需要考虑的一件事是为什么你有这么多子句。 正如 SWITCH 语句通常表明您应该将选择移至子类中一样,大量复杂的 IF 语句链可能表明您在一个地方组合了太多概念(以及决策)。

作为例子,我将使用计算佣金的例子。 在我构建的一个系统中,佣金率取决于给予批发商的佣金金额,批发商将一些佣金转嫁给零售商。 批发商给零售商的金额取决于零售商和批发商之间的具体协议(基于合同)。 批发商获得的金额同样取决于产品线、具体产品和销售产品的数量。

除此之外,还有基于客户状态、特定产品定制等的额外“例外”规则。这都可以通过复杂的 IF 语句链来解决,但我们改为使用应用程序数据驱动的。

通过将所有这些信息放入表格中,我们就能够传递数据规则。 首先将触发批发商的通用规则,然后将触发任何覆盖规则。 然后,掌握了基本批发商佣金后,我们将运行批发商到零售商的通用规则,然后是这些规则的例外情况。

这将庞大的逻辑变成了一个简单的四步过程,每个步骤只需进行数据库查询即可找到要应用的正确规则。

这当然可能不适用于您的情况,但通常像这样的大型复合体实际上意味着要么没有足够的类来划分责任(我们问题的另一个解决方案可能是包含特定规则和覆盖的工厂类),或者功能应该是数据驱动的。

One thing to consider is why you have so many clauses. Just as SWITCH statements often indicate that you should be moving the choices into subclasses, a large complex chain of IF statements can indicate that you are combining too many concepts (and thus decisions) in one place.

As an example, I will use the example of calculating commission. In one system I built, the commission rate is dependent upon the amount of commission given to the wholesaler, who passes some on to the retailer. The amount given from a wholesaler to retailer depends on the specific agreement between the retailer and the wholesaler (based on a contract). The amount the wholesaler gets likewise depends on the product line, specific product and amount of the product sold.

Beyond this, there were additional "exception" rules based on the customer's state, specific product customizations, etc. This could all be worked out in a complex chain of IF statements, but we instead made the application data driven.

By putting all of this information into tables, we were then able to make passes on the data rules. First the wholesaler's generic rules would trigger and then any override rules would trigger. Then, having the base wholesaler commission in hand we would run the wholesaler to retailer generic rules and then the exceptions to those.

This turned a huge hairball of logic into a simple four step process, with each step simply making a database query to find the correct rule to apply.

This of course may not apply in your situation, but often a large complex like that really means that either there are not enough classes dividing up the responsibility (another solution to our problem could have been factory classes that contained the specific rules and overrides), or the functionality should be data driven.

暖风昔人 2024-07-31 06:36:59

将其重构为函数。

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

或者,您可以在 Check 函数中构建更具可读性的代码/逻辑。

Refactor it to a function.

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

Alternatively, you can build more readable code/logic in your Check function.

李白 2024-07-31 06:36:59

有很多方法可以解决这个问题,但让我选择一些。

首先,存在一种情况,即所有条件(if 语句中的所有 AND 条件)+ 如果它们都为真则执行的代码是一次性情况。

在这种情况下,请使用您拥有的代码。 您可能想要执行其他几个人已经建议的操作,重写以使用 Guard 子句类型的代码。

换句话说,不是这样:

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

... 你重写为类似这样的内容:

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

为什么? 因为一旦你开始将 OR 条件引入其中,就很难阅读代码并弄清楚将会发生什么。 在上面的代码中,您将条件拆分为每个 AND 运算符 (&&),因此代码变得更易于阅读。 基本上,您将代码从“如果这个和那个,或者那个其他事情和那个第三件事,或者其他一些事情,然后做某事”重写为“如果这个,那么退出;如果那个其他事情;然后退出;如果某个其他事情;然后退出;如果以上都不是,则做某事”。

然而,在很多情况下,你也会遇到可重用性的情况。 如果其中一些标准出现在其他地方,但实际执行的代码 (DoSomething) 不同,那么我会再次采用其他人已经建议的内容。 将条件重写为根据评估条件的结果返回布尔值结果的方法。

例如,什么更容易阅读,这个?

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();
}

我想说第二种方法比第一种方法更容易阅读,并且更可重用。

There are many ways to handle this, but let me pick a few.

First, there's the case where all of the criteria (all of the AND-criteria in your if-statement) + the code to execute if they're all true is a one-off situation.

In this case, use the code you have. You might want to do what several others have already suggested, rewrite to use a Guard-clause type of code.

In other words, instead of this:

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

... you rewrite to something similar to this:

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

Why? Because once you start introducing OR-criteria into the mix, it gets hard to read the code and figure out what is going to happen. In the above code, you split the criteria on each AND-operator (&&), and thus the code becomes easier to read. Basically you rewrite the code from saying "if this and that, or that other thing and that third thing, or some other thing, then do something" to be "if this, then exit; if that other thing; then exit; if some other thing; then exit; if none of the above, do something".

However, in many cases, you also have the case of reusability. If some of those criteria appear else-where, but the code that will actually execute (DoSomething) isn't the same, then I'd go for, again, what others have already suggested. Rewrite the criteria into methods that return a Boolean result depending on the result of evaluating the criteria.

For instance, what is easier to read, this?

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

or this:

if (CanAccessStream() && CanWriteToStream())

assuming all those letters can be split into those two criteria.

In that case I would take some of the criteria and put into those methods, and choose an appropriate name for the criteria.

The third option is where the criteria differs in several places in the code, but the actual code to execute is the same.

In this case I would rewrite so that you group criteria together and layer the methods so that calling one method will check some criteria, and then call another method, which will check some other criteria, etc.

For instance, you could write this:

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

or you could write this:

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();
}

I'd say the second way is easier to read, and is more reusable, than the first.

倦话 2024-07-31 06:36:59

对您的值列表执行聚合操作。

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

*编辑:给数据添加额外的行:

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

Perform aggregate operations on a list of your values.

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

*Edit: Givin' the data an extra-line:

var Data = new[] { something, somethingelse, ... };
if (Data.All(x => x == -1)) {
}
心房的律动 2024-07-31 06:36:59

看起来您有 3 条信息,它们一起代表应用程序中的特定状态。 与其打开这三个状态,为什么不创建一个封装它们的值呢? 然后,您可以在创建时使用对象层次结构或委托来绑定您尝试运行的操作。

It looks like you have 3 pieces of information that together represent a particular state in your application. Instead of switching on these 3 pieces of state, why not create a value that encapsulates them? Then you could use an object hierarchy or a delegate at creation time to bind the action you are trying to run.

安静被遗忘 2024-07-31 06:36:59

您可以将其重构为函数,并返回表示正确情况的枚举值:

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

You could refactor it as a function, and return an Enum value that represents the case that was true:

if(something != -1)
    return MyEnum.Something;
if(somethingelse != -1)
    return MyEnum.SomethingElse;
if(etc != -1)
    return MyEnum.SomethingElseEntirely;
return MyEnum.None;
许仙没带伞 2024-07-31 06:36:59

您还可以将状态纳入一个类中。

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

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

You could also factor the state into a class.

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

    bool abletodostuff()
    {
        return (something == -1) && (somethingelse == -1) && (etc == -1);
    }
}
_畞蕅 2024-07-31 06:36:59

我不知道 C#,但它似乎包含 条件运算符。 如果你的条件很短,你可以用漂亮的表状结构替换长的 if/elsif/else 链,如下所示:

return   something == 0      ? 0
       : somethingelse == -1 ? 1
       : yetanotherthing > 2 ? 2
       :                       3; # default

I don't know C#, but it seems to include the conditional operator. If your conditions are short, you can replace long if/elsif/else chains with nice table-like structures, like this:

return   something == 0      ? 0
       : somethingelse == -1 ? 1
       : yetanotherthing > 2 ? 2
       :                       3; # default
念三年u 2024-07-31 06:36:59

您可以探索的另一个途径是使用复合表达式。 这些表达式(这是 LINQ 在 .NET 框架中工作的基础)允许您基于所有这些条件创建表达式树,然后业务逻辑代码可以简单地对顶级表达式进行操作以获取 true/false 结果。

要评估表达式,您可以使用 访问者模式

这使您可以轻松构建树的条件。 这些树甚至可以序列化,让您保留做出决定的条件。 这里有很多机会。

Yet another avenue that you may explore is the use of composite expressions. These expressions (which are the basis of how LINQ works in the .NET framework) allows you to create expression trees based on all these conditions and then the business logic code can simply operate on the top level expression to get back the true/false result.

To evaluate the expressions, you can make use of the visitor pattern

This allows you to easily compose trees of the conditions. these trees can be serialized even to let you persist the conditions that you made the decision under. Lots of opportunities present themselves here.

北恋 2024-07-31 06:36:59

在一个“if”中看到多个子句的情况并不常见。 当您需要执行某些行而不考虑某些条件的真实性时,您通常会发现需要嵌套“if”才能获得所需的逻辑。 如果不需要的话,如果它们都需要同时进行测试,我并不是说将它们嵌套起来。 仅当有一些通用功能时。
另一个考虑因素是使用某些条件集的结果设置一个布尔变量,这可能会更容易理解。
如果您的变量是数组或集合,您可以循环遍历它们吗? 您是否将它们全部针对 -1 进行测试?

It's not that common to see that many clauses in one "if". You usually find that you need to nest the "ifs" to get the logic you need, when you need to execute some line regardless of the truth of some conditions. I'm not saying nest them if you don't need to, if they all need to be tested at the same time. Only if there's some common functionality.
Another consideration is to set a boolean variable with the result of some set of these conditions, that might make it easier to understand.
If your variables are an array or collection, could you loop through them? Are you testing them all against -1?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文