9

Ever since reading Clean Code I have been trying to keep my code descriptive and easy to understand. I have a condition where either A or B must be filled in. But not both. And not neither. Currently the if statement to check for this condition is hard to follow at a glance. How would you write the following to make it clear at a glance what is being checked

if ((!string.IsNullOrEmpty(input.A) && !string.IsNullOrEmpty(input.B)) 
    || string.IsNullOrEmpty(input.A) && string.IsNullOrEmpty(input.B))
{
    throw new ArgumentException("Exactly one A *OR* B is required.");
}
Pretzel
  • 8,141
  • 16
  • 59
  • 84
CaffGeek
  • 21,856
  • 17
  • 100
  • 184
  • 1
    You want the answer for Java code or just any code? – Claus Broch Jul 12 '10 at 13:48
  • I know this is not what you're asking, and is an entirely different discussion which has been rehashed interminably, but I don't think I'd throw an exception here... maybe a simple message to the user? – MickeyfAgain_BeforeExitOfSO Jul 12 '10 at 13:51
  • @Claus, I'm actually writing C#, but the language shouldn't matter to the question – CaffGeek Jul 12 '10 at 13:51
  • @mickey, it's in a Unit Test, an exception causes it to fail, so it's fine – CaffGeek Jul 12 '10 at 13:52
  • 1
    Wow! so many people eager to explain XOR! – Anax Jul 12 '10 at 13:52
  • @Chad: Actually the language might make a big difference to the answer since this could already be directly supported. – Claus Broch Jul 12 '10 at 13:53
  • 2
    @Chad: Due to subtleties of type (e.g. who says 'null' is a valid value?), operators (is it !=, ^, or xor? – actually all three work in C++ :P), and conversions, language is important, even thought the basic concept is the same. –  Jul 12 '10 at 14:00
  • @maxwellb Mostly surprised; we are supposed to work against information redundancy. – Anax Jul 12 '10 at 14:10

8 Answers8

24

Time for an XOR:

if(!(string.IsNullOrEmpty(input.A) != string.IsNullOrEmpty(input.B)))
    throw new ArgumentException("Exactly one A *OR* B is required.");

You may also see it written as:

if(!(string.IsNullOrEmpty(input.A) ^ string.IsNullOrEmpty(input.B)))
    throw new ArgumentException("Exactly one A *OR* B is required.");
Justin Niessner
  • 242,243
  • 40
  • 408
  • 536
  • 11
    XOR for boolean expressions can also be written as `!=`. And since `^` is rarely used in boolean expressions (at least in the Java code I've seen), I'd actually suggest using `!=` as every developer knows what that means. – Joachim Sauer Jul 12 '10 at 13:51
  • Also see this: http://stackoverflow.com/questions/160697/is-it-good-practice-to-use-the-xor-operator-in-java-for-boolean-checks – Janick Bernet Jul 12 '10 at 13:52
  • 3
    @Joachim; only true if you have only one truth value, which in C you don't (anything non-zero is true). – falstro Jul 12 '10 at 13:52
  • @roe: you're correct. I was thinking of Java code only. But to be honest most modern languages have a boolean data type and those **do** have only 1 value for `true` and 1 value for `false`. – Joachim Sauer Jul 12 '10 at 13:54
  • @roe: Many languages treat non-zero as true, that's why you use a boolean data type. –  Jul 12 '10 at 13:58
  • 1
    Is the negation of each operand really needed? I suppose it might depend on the readability for each specific statement, but trimming some punctionation might be nice. – Mark Peters Jul 12 '10 at 14:03
  • Some languages even treat zero as true (Ruby). – Tim Pietzcker Jul 12 '10 at 14:04
  • @Mark Peters - No. Not needed. Just typed it in as I was composing the answer and never removed it. It's gone now. – Justin Niessner Jul 12 '10 at 14:05
  • 1
    @Mark Peters (answering my own question): I suppose it's necessary in a language that has multiple values for `true`, since the negation will normalize to zero or at least a consistent value for `true`. – Mark Peters Jul 12 '10 at 14:06
  • @Justin; Your solution is actually not correct. What your IF checks it the success case (one is valid, other not), but it then throws the exception. You would have to negate the whole statement. – Hendrik Jul 14 '10 at 10:13
  • @Hendrik - You are correct. Not sure exactly how that happened, but it is fixed. – Justin Niessner Jul 14 '10 at 12:17
13
if (string.IsNullOrEmpty(input.A) != string.IsNullOrEmpty(input.B)) {
 // do stuff
}
Nordic Mainframe
  • 28,058
  • 10
  • 66
  • 83
  • 2
    this is only correct if the truth value is always the same, which in C it doesn't have to be. 1 != 2 is true, but should be logically XORed to false. – falstro Jul 12 '10 at 13:54
  • @Roger; no, 1 != 2 -> true, 1 logical-XOR 2 -> false, since 1 and 2 are both true. – falstro Jul 12 '10 at 13:56
  • @Chad: I know you wanted to escape with throwing `ArugmentException`, but often times you will want to continue-if-valid, which @Luther Blissett gives an example of. You could, with this answer, add an `else { throw ... }`. – maxwellb Jul 12 '10 at 14:00
  • My example is correct for Java and CSharp, where predicates return the boolean type. The !^! technique works for C where bool is expressed as a specific condition on integers or pointer types. It does not work under Java or CSharp, because your can't apply ! or && or || to integer types there. – Nordic Mainframe Jul 12 '10 at 14:11
12

Its an XOR, and its really easy to emulate.

Just to think about it:

Both cannot be true, both cannot be false. One has to be true, one has to be false.

So, we come to this:

if(string.IsNullOrEmpty(input.A) == string.IsNullOrEmpty(input.B)) {
   throw new ArgumentException("Exactly one A *OR* B is required.");
}

If both are equal, they are either both true, or both false. And both cases are invalid.

And all that without any special XOR operator that the language of choice might not have. ;)

Hendrik
  • 1,981
  • 16
  • 11
6

This relationship is called exclusive-or (xor).

Some languages provide it as an operator -- typically ^:

True ^ True -> False
True ^ False -> True
False ^ True -> True
False ^ False -> False
Donald Miner
  • 38,889
  • 8
  • 95
  • 118
3

Use an exclusive-OR: A XOR B

Mitch Wheat
  • 295,962
  • 43
  • 465
  • 541
2

what you're looking for is XOR ( http://en.wikipedia.org/wiki/Exclusive_or ) logic.

You can write it as:

if (string.IsNullOrEmpty(A) ^ string.IsNullOrEmpty(B))
{
//Either one or the other is true
}
else
{
//Both are true or both are false
}
Ian Jacobs
  • 5,456
  • 1
  • 23
  • 38
1

What you need is called XOR i.e. exclusive OR operation.

Truth table will reveal it to you ;)

A   B   ⊕
F   F   F
F   T   T
T   F   T
T   T   F

In some languages(or in most of them) it is denoted by A ^ B.

good wiki article

Pratik Deoghare
  • 35,497
  • 30
  • 100
  • 146
0

This is the very definition of exclusive or. There are a bunch of ways using boolean algebra, the simplest one is to use a XOR operator. In C, there's no logical xor though, but you can use the binary one, doubling the not operator to force any truth value to be one (as in 0x01)

!!string.IsNullOrEmpty(input.A) ^ !!string.IsNullOrEmpty(input.B)

Or do the negative test

!string.IsNullOrEmpty(input.A) ^ !string.IsNullOrEmpty(input.B)

which will be true if both A and B are set, or neither.

falstro
  • 34,597
  • 9
  • 72
  • 86
  • @Roger; I'll put it here as well then. No it isn't, only in languages where you only have one truth value. != means not equal, 1 and 2 are not equal, yet they're both true, so logical xor yields false. You may do !!A != !!B, but that's just pushing it when you do have the xor operator in that case... ;) – falstro Jul 12 '10 at 13:58
  • @roe: Use a boolean data type. –  Jul 12 '10 at 14:05
  • @Roger; note that my answer is in C, where there is no boolean data type, hence "In C there is no logical xor". – falstro Jul 12 '10 at 14:09
  • "In logic and mathematics, a logical value, also called a truth value, is a value indicating the relation of a proposition to truth." ( [WP](http://en.wikipedia.org/wiki/Logical_value) ) When I say "logical xor" I'm talking about logical (boolean) values, not ints. –  Jul 12 '10 at 14:12
  • @Roger; See, the missing piece here is that logical != boolean. There is more than one truth value (your WP article speaks of classical logic where there's just true and false, implying there might be others, don't you think?), and not-equal doesn't suggest exclusive-or. The point is moot anyway, since most library functions do not use just 1 or 0 as a return value, but rather 0 for success and non-zero (usually a fault code) for failure, in which case != isn't a logical xor. As for your "When I say", well good for you, the point is that you're commenting on stuff which doesn't say that. – falstro Jul 13 '10 at 07:08