## #1,074 – Use Parentheses in Expressions to Make Code More Readable

In the absence of parentheses within an expression, operator precedence and associativity dictate how the expression will be evaluated. Technically, you only need parentheses in the expression if you want the expression to be evaluated differently, relative to the precedence and associativity rules.

If your expression doesn’t require parentheses in order to evaluate correctly, it’s often still a good idea to include them.  The parentheses will typically improve the readability of the expression because they make the evaluation order more clear.

// This is tough to read
int i5 = 1 + 10 / 5 * 2 - 12 / 4 + 24 % 5 / 2 * 4;

// This is a bit better
i5 = 1 + ((10 / 5) * 2) - (12 / 4) + (((24 % 5) / 2) * 4);

## #1,073 – Arithmetic Binary Operators are Left-Associative

All arithmetic binary operators (+, -, *, /, %) are left-associative.  This means that when there are multiple operators having the same precedence, the expression is evaluated from left to right.  Below are some examples.

// Multiplicative, left-right

// (10 / 5) * 2 = 4
// [result would be 1 if right-associative]
int i = 10 / 5 * 2;

// (40 % 12) * 2 = 8
// [result would be 16 if right-associative]
int i2 = 40 % 12 * 2;

// (4 - 3) + 5 = 6
// [result would be -4 if right-associative]
int i3 = 4 - 3 + 5;

Note that the multiplicative operators (*, /, %) have a higher precedence than the additive (+, -).  This means that if there are no parentheses, the multiplicative operators are evalated before the arithmetic.

// 1 + (2 * 3) = 7
int i4 = 1 + 2 * 3;

// Equivalent to 1 + ((10 / 5) * 2) = 5
int i5 = 1 + 10 / 5 * 2;

## #1,072 – How the Unary Minus Operator Can Fail

There are cases when applying a unary operator to an operand results in a value that does not fit into the data type of the operand. Consider the int type, whose range is -2,147,483,648 to 2,147,483,647.  If we assign the minimum value (largest negative number) and try to negate it with the unary operator, the negation will fail.

By default, arithmetic operations work in an unchecked context.  In this case, the unary operator does nothing, just returning the value of the original operand.

// int range is -2,147,483,648 to 2,147,483,647
int n = int.MinValue;
n = -n;  // n unchanged

If we do the same operation in a checked context, we’ll get an overflow exception.

Note that a cast to a “larger” data type fails if it’s done outside of the unary operator.  But casting before applying the operator does work.

long l = (long)-n;  // Still fails

long l2 = -(long)n;  // This works

## #1,071 – The Unary Minus Operator

Unary operators are operators that affect a single operand.

The unary – operator is used to negate a numeric value.  (Subtract the value from zero).  The unary + operator does not affect a value, so is not worth mentioning.

// Unary minus operator applied to constants
int x = 5;
int y = -5;
Console.WriteLine("x={0}, y={1}", x, y);

// Unary minus operator applied to variable
x = -x;
y = -y;
Console.WriteLine("x={0}, y={1}", x, y);

The unary operator also works with floating point and decimal values.

double d = -4.2;
float f = -4.2f;
Console.WriteLine("d={0}, f={0}", d, f);

decimal m = -0.01m;
Console.WriteLine("m={0}", m);

## #592 – Optional Parameters in Indexers

In addition to defining optional parameters within methods, you can also include an optional parameter in an indexer.

For example, assume that we have a Dog class that keeps track of the sounds that a dog makes when he barks and that we’ve defined an indexer for the class that retrieves a “bark sound” from that list.

public string this[int i]
{
get
{
return BarkRecord[i];
}
}

Using the indexer:

kirby.Bark("Woof");
kirby.Bark("Bow-wow");
kirby.Bark("Urgh");

Console.WriteLine(kirby[2]);

We can now add an optional parameter to the indexer, to indicate that we’d like the date/time of the bark returned as well.

public string this[int i, bool includeTime = false]
{
get
{
string barkInfo = BarkRecord[i];

if (includeTime)
barkInfo = barkInfo + string.Format(" at {0}", BarkTime[i]);

return barkInfo;
}
}

Using the indexer:

Console.WriteLine("Bark at [2]={0}", kirby[2]);
Console.WriteLine("Bark at [1]={0}", kirby[1, true]);

## #580 – as Operator Can Generate Compile-Time Errors

The as operator can perform reference conversions between types in a type hierarchy, returning null if the conversion is not possible.  But there are some cases when the compiler knows that no conversion is possible between the specified types, so it will generate an error at compile time.

Dog d = new Dog("Lassie", 12);

// Can't convert a Dog to a Cow--compile-time error
Cow c = d as Cow;

## #579 – Typical Pattern for Using as Operator

When you use the as operator to attempt to convert an expression to a particular type, you typically follow the pattern shown below.

In most cases, you have a variable of a base class that stores some object and you use the as operator to determine if it stores an object of a particular derived type.  In the example below, Terrier is a class that inherits from Dog.

// Dog variable referring to object of type Terrier
Dog d = new Terrier("Jack", 17, "Crabby");

// Elsewhere in our code, we have variable
// of type Dog and want to see if it refers to a
// Terrier.
Terrier t = d as Terrier;
if (t != null)
t.TerrierMethod();

You can do the same thing with the is operator, though it is a little less efficient, because it actually does the type conversion twice.

// A bit less efficient
if (d is Terrier)
((Terrier)d).TerrierMethod();

## #578 – Using the as Operator to Do Type Conversions

The as operator attempts to convert an expression to a specified type, returning the value of null if the conversion fails.  It behaves similarly to doing the conversion using a cast, but does not throw an exception if the cast fails.

// (Terrier is a sub-class of Dog)

Dog d = new Dog("Fido", 5);
Terrier t = new Terrier("Jack", 17, "Crabby");
Dog d2 = t;

Dog dTest = d as Dog;   // ok
dTest = d as Terrier;   // null

dTest = t as Dog;       // ok
Terrier tTest = t as Terrier;   // ok

dTest = d2 as Dog;      // ok
tTest = d2 as Terrier;  // ok

Object oTest = d as object;    // ok

## #577 – Using the is Operator to Check for an Unboxing Conversion

You can use the is operator to check to see whether an object can be unboxed to a particular type.  The operator will return true if the object contains a boxed instance of the specified type.

int i = 12;
double d1 = 4.2;

object o1 = i;   // Boxed int
object o2 = d1;  // Boxed double
object o3 = new object();  // Some object

bool check;

check = o1 is int;       // true - object contains a boxed int
check = o2 is int;       // false - object contains boxed double
check = o3 is int;       // false

## #576 – Using the is Operator with Value Typed Objects

You can use the is operator to check the type of value-typed objects.  The result of a check using the is operator is true if the type of the expression matches the specified type exactly.  Because value types don’t support inheritance, an object of one type will never return true for the is operator on a different type, even if the value is assignment compatible with the specified type.

bool check;

byte b = 1;
short s = 2;

check = b is byte;     // true
check = b is short;    // false

// Assignment succeeds
s = b;        // short <= byte

check = s is short;    // true
check = s is byte;     // false

Because all value types inherit from System.ValueType and, indirectly, from System.Object, the is operator always returns true when checking against these types.

check = b is object;   // true
check = s is object;   // true
check = b is System.ValueType;   // true
check = s is System.ValueType;   // true