开发者

Methods of simplifying ugly nested if-else trees in C#

开发者 https://www.devze.com 2022-12-09 14:25 出处:网络
Sometimes I\'m writing ugly if-else statements in C# 3.5; I\'m aware of some different approaches to simplifying that with table-driven development, class hierarchy, anonimous methods and some more.

Sometimes I'm writing ugly if-else statements in C# 3.5; I'm aware of some different approaches to simplifying that with table-driven development, class hierarchy, anonimous methods and some more. The problem is that alternatives are still less wide-spread than writing traditional ugly if-else statements because there is no convention for that.

What depth of nested if-else is normal for C# 3.5? What methods do you expect to see instead of nested if-else the first? the second?

if i have ten input parameters with 3 states in each, i should map functions to combination of each state of each parameter (really 开发者_运维问答less, because not all the states are valid, but sometimes still a lot). I can express these states as a hashtable key and a handler (lambda) which will be called if key matches.

It is still mix of table-driven, data-driven dev. ideas and pattern matching.

what i'm looking for is extending for C# such approaches as this for scripting (C# 3.5 is rather like scripting) http://blogs.msdn.com/ericlippert/archive/2004/02/24/79292.aspx


Good question. "Conditional Complexity" is a code smell. Polymorphism is your friend.

Conditional logic is innocent in its infancy, when it’s simple to understand and contained within a few lines of code. Unfortunately, it rarely ages well. You implement several new features and suddenly your conditional logic becomes complicated and expansive. [Joshua Kerevsky: Refactoring to Patterns]

One of the simplest things you can do to avoid nested if blocks is to learn to use Guard Clauses.

double getPayAmount() {
if (_isDead) return deadAmount();
if (_isSeparated) return separatedAmount();
if (_isRetired) return retiredAmount();
return normalPayAmount();
};  

The other thing I have found simplifies things pretty well, and which makes your code self-documenting, is Consolidating conditionals.

double disabilityAmount() {
    if (isNotEligableForDisability()) return 0;
    // compute the disability amount

Other valuable refactoring techniques associated with conditional expressions include Decompose Conditional, Replace Conditional with Visitor, Specification Pattern, and Reverse Conditional.


There are very old "formalisms" for trying to encapsulate extremely complex expressions that evaluate many possibly independent variables, for example, "decision tables" :

http://en.wikipedia.org/wiki/Decision_table

But, I'll join in the choir here to second the ideas mentioned of judicious use of the ternary operator if possible, identifying the most unlikely conditions which if met allow you to terminate the rest of the evaluation by excluding them first, and add ... the reverse of that ... trying to factor out the most probable conditions and states that can allow you to proceed without testing of the "fringe" cases.

The suggestion by Miriam (above) is fascinating, even elegant, as "conceptual art;" and I am actually going to try it out, trying to "bracket" my suspicion that it will lead to code that is harder to maintain.

My pragmatic side says there is no "one size fits all" answer here in the absence of a pretty specific code example, and complete description of the conditions and their interactions.

I'm a fan of "flag setting" : meaning anytime my application goes into some less common "mode" or "state" I set a boolean flag (which might even be static for the class) : for me that simplifies writing complex if/then else evaluations later on.

best, Bill


Simple. Take the body of the if and make a method out of it.

This works because most if statements are of the form:

if (condition):
   action()

In other cases, more specifically :

if (condition1):
   if (condition2):
      action()

simplify to:

if (condition1 && condition2):
   action()


I'm a big fan of the ternary operator which get's overlooked by a lot of people. It's great for assigning values to variables based on conditions. like this

foobarString = (foo == bar) ? "foo equals bar" : "foo does not equal bar";

Try this article for more info.

It wont solve all your problems, but it is very economical.


I know that this is not the answer you are looking for, but without context your questions is very hard to answer. The problem is that the way to refactor such a thing really depends on your code, what it is doing, and what you are trying to accomplish. If you had said that you were checking the type of an object in these conditionals we could throw out an answer like 'use polymorphism', but sometimes you actually do just need some if statements, and sometimes those statements can be refactored into something more simple. Without a code sample it is hard to say which category you are in.


I was told years ago by an instructor that 3 is a magic number. And as he applied it it-else statements he suggested that if I needed more that 3 if's then I should probably use a case statement instead.

   switch (testValue)
   {
      case = 1:
         // do something
         break;
      case = 2:
         // do something else
         break;
      case = 3:
         // do something more
         break;
      case = 4
         // do what?
         break;
      default:
         throw new Exception("I didn't do anything");
   }

If you're nesting if statements more than 3 deep then you should probably take that as a sign that there is a better way. Probably like Avirdlg suggested, separating the nested if statements into 1 or more methods. If you feel you are absolutely stuck with multiple if-else statements then I would wrap all the if-else statements into a single method so it didn't ugly up other code.


If the entire purpose is to assign a different value to some variable based upon the state of various conditionals, I use a ternery operator.

If the If Else clauses are performing separate chunks of functionality. and the conditions are complex, simplify by creating temporary boolean variables to hold the true/false value of the complex boolean expressions. These variables should be suitably named to represent the business sense of what the complex expression is calculating. Then use the boolean variables in the If else synatx instead of the complex boolean expressions.


One thing I find myself doing at times is inverting the condition followed by return; several such tests in a row can help reduce nesting of if and else.


Not a C# answer, but you probably would like pattern matching. With pattern matching, you can take several inputs, and do simultaneous matches on all of them. For example (F#):

let x=
  match cond1, cond2, name with
  | _, _, "Bob"     -> 9000 // Bob gets 9000, regardless of cond1 or 2
  | false, false, _ -> 0 
  | true, false, _  -> 1
  | false, true, _  -> 2
  | true, true, ""  -> 0 // Both conds but no name gets 0
  | true, true, _   -> 3 // Cond1&2 give 3

You can express any combination to create a match (this just scratches the surface). However, C# doesn't support this, and I doubt it will any time soon. Meanwhile, there are some attempts to try this in C#, such as here: http://codebetter.com/blogs/matthew.podwysocki/archive/2008/09/16/functional-c-pattern-matching.aspx. Google can turn up many more; perhaps one will suit you.


try to use patterns like strategy or command


In simple cases you should be able to get around with basic functional decomposition. For more complex scenarios I used Specification Pattern with great success.

0

精彩评论

暂无评论...
验证码 换一张
取 消