views:

473

answers:

8

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.");
}
+22  A: 

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
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
Also see this: http://stackoverflow.com/questions/160697/is-it-good-practice-to-use-the-xor-operator-in-java-for-boolean-checks
inflagranti
@Joachim; only true if you have only one truth value, which in C you don't (anything non-zero is true).
roe
@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
@roe: Many languages treat non-zero as true, that's why you use a boolean data type.
Roger Pate
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
Some languages even treat zero as true (Ruby).
Tim Pietzcker
@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
@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
@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
@Hendrik - You are correct. Not sure exactly how that happened, but it is fixed.
Justin Niessner
+3  A: 

Use an exclusive-OR: A XOR B

Mitch Wheat
+6  A: 

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
orangeoctopus
A: 

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.

roe
!= is logical xor.
Roger Pate
@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... ;)
roe
@roe: Use a boolean data type.
Roger Pate
@Roger; note that my answer is in C, where there is no boolean data type, hence "In C there is no logical xor".
roe
@roe: #include <stdbool.h>
Roger Pate
"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.
Roger Pate
@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.
roe
+2  A: 

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
+14  A: 
if (string.IsNullOrEmpty(input.A) != string.IsNullOrEmpty(input.B)) {
 // do stuff
}
Luther Blissett
c'mon people! bit wise xor?
Luther Blissett
^ This is the correct answer.
orangeoctopus
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.
roe
!= is logical xor.
Roger Pate
@Roger; no, 1 != 2 -> true, 1 logical-XOR 2 -> false, since 1 and 2 are both true.
roe
@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
Luther Blissett
+11  A: 

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 just because you didn't use an inequality operator.
Christian Severin
+1 for the easiest solution. You don't need to double negate the expression.
Luiz Damim
+1 thank you. So many double negations in the other answers I was beginning to doubt my sanity.
Muhammad Alkarouri
+1  A: 
TheMachineCharmer