By Joe Mayo
Source: C# Unleashed
Chapter 3: Writing C#
Expressions
In This Chapter
- Unary Operators
- Binary Operators
- The Ternary Operator
- Other Operators
- Enumeration Expressions
- Array Expressions
- Statements
- Blocks
- Labels
- Declarations
- Operator Precedence and Associativity
C# provides a complete set of language elements for writing expressions.
An expression is a set of language elements combined to perform a meaningful
computation. This chapter provides guidance in building C# expressions.
This chapter demonstrates expressions created with each of C#'s built-in
operators. All aspects of operators are covered in order to provide an
understanding of their effects.
There are four types of operatorsunary, binary, ternary, and a few
others that don't fit into a category. Unary operators affect a single
expression. Binary operators require two expressions to produce a result. The
ternary operator has three expressions. The others can only be explained by
reading each of their descriptions.
For C++ and Java Programmers
C#
operators and their precedence are the same. No surprises here at all. If
desired, you could skip this section without missing anything.
Unary Operators
As previously stated, unary operators affect a single expression. In many
instances, the unary operators enable operations with simpler syntax than a
comparable binary operation. The unary operators include + (plus),
- (minus), ++ (increment), -- (decrement), !
(logical negation), and ~ (bitwise complement).
Note
Mathematical operations on
floating-point types are performed according to IEEE 754 arithmetic.
The Plus Operator
The plus operator (+) has no effect on the expression it's used
with. Why would a language have an operator that has no effect? For consistency.
Most C# operators have a logical complement. Since there is a minus operator,
its logical complement is the plus operator. The + operator is
available to explicitly document code. Here are a couple examples:
int negative = -1;
int positive = 1;
int result;
result = +negative; // result = -1
result = +positive; // result = 1
The Minus Operator
The minus operator (-) allows negation of a variable's value.
In integer and decimal types, the result is the number subtracted from zero. For
floating-point types, the - operator inverts the sign of the number.
When a value is NaN (not a number), the result is still NaN.
Here are some examples:
int negInt = -1;
decimal posDec = 1;
float negFlt = -1.1f;
double nanDbl = Double.NaN;
int resInt;
decimal resDec;
float resFlt;
double resDbl;
resInt = -negInt; // resInt = 1
resDec = -posDec; // resDec = -1
resFlt = -negFlt; // resFlt = 1.1
resDbl = -nanDbl; // resDbl = NaN
The Increment Operator
The increment operator (++) allows incrementing the value of a
variable by 1. The timing of the effect of this operator depends upon which side
of the expression it's on.
Here's a post-increment example:
int count;
int index = 6;
count = index++; // count = 6, index = 7
In this example, the ++ operator comes after the expression
index. That's why it's called a post-increment operator. The
assignment takes place and then index is incremented. Since the
assignment occurs first, the value of index is placed into
count, making it equal 6. Then index is incremented to become
7.
Here's an example of a pre-increment operator:
int count;
int index = 6;
count = ++index; // count = 7, index = 7
This time the ++ operator comes before the expression
index. This is why it's called the pre-increment operator.
Index is incremented before the assignment occurs. Since index
is incremented first, its value becomes 7. Next, the assignment occurs to make
the value of count equal 7.
The Decrement Operator
The decrement operator (--) allows decrementing the value of a
variable. The timing of the effect of this operator again depends upon which
side of the expression it is on. Here's a post-decrement example:
int count;
int index = 6;
count = index--; // count = 6, index = 5
In this example, the -- operator comes after the expression
index, and that's why it's called a post-decrement operator.
The assignment takes place and then index is decremented. Since the
assignment occurs first, the value of index is placed into
count, making it equal 6. Then index is decremented to become
5.
Here's an example of a pre-decrement operator:
int count;
int index = 6;
count = --index; // count = 5, index = 5
This time the -- operator comes before the expression
index, which is why it's called the pre-decrement operator.
Index is decremented before the assignment occurs. Since index
is decremented first, its value becomes 5, and then the assignment occurs to
make the value of count equal 5.
The Logical Complement Operator
A logical complement operator (!) serves to invert the result of a
Boolean expression. The Boolean expression evaluating to true will be
false. Likewise, the Boolean expression evaluating to false
will be true. Here are a couple examples:
bool bexpr = true;
bool bresult = !bexpr; // bresult = false
bresult = !bresult; // bresult = true
The Bitwise Complement Operator
A bitwise complement operator (~) inverts the binary representation
of an expression. All 1 bits are turned to 0. Likewise, all 0 bits are turned to
1. Here's an example:
byte bitComp = 15; // bitComp = 15 = 00001111b
byte bresult = (byte) ~bitComp; // bresult = 240 = 11110000b
Binary Operators
Binary operators are those operators that work with two operands. For
example, a common binary expression would be a + bthe addition
operator (+) surrounded by two operands. The binary operators are
further subdivided into arithmetic, relational, logical, and assignment
operators.
Arithmetic Operators
This is the first group of binary operators, those supporting arithmetic
expressions. Arithmetic expressions are composed of two expressions with an
arithmetic operator between them. This includes all the typical mathematical
operators as expected in algebra.
The Multiplication Operator
The multiplication operator (*) evaluates two expressions and
returns their product. Here's an example:
int expr1 = 3;
int expr2 = 7;
int product;
product = expr1 * expr2; // product = 21
The Division Operator
The division operator (/), as its name indicates, performs
mathematical division. It takes a dividend expression and divides it by a
divisor expression to produce a quotient. Here's an example:
int dividend = 45;
int divisor = 5;
int quotient;
quotient = dividend / divisor; // quotient = 9
Notice the use of integers in this expression. Had the result been a
fractional number, it would have been truncated to produce the integer
result.
The Remainder Operator
The remainder operator (%) returns the remainder of a division
operation between a dividend and divisor. A common use of this operator is to
create equations that produce a remainder that falls within a specified range.
Here's an example:
int dividend = 33;
int divisor = 10;
int remainder;
remainder = dividend % divisor; // remainder = 3
No matter what, as long as the divisor stays at 10, the remainder will always
be between 0 and 9.
The Addition Operator
The addition operator (+) performs standard mathematical addition by
adding one number to another. Here's an example:
int one = 1;
int two;
two = one + one; // two = 2
The Subtraction Operator
The subtraction operator (-) performs standard mathematical
subtraction by subtracting the value of one expression from another. Here's
an example:
decimal debt = 537.50m;
decimal payment = 250.00m;
decimal balance;
balance = debt - payment; // balance = 287.50
The Left Shift Operator
To shift the bits of a number to the left, use the left shift operator
(<<). The effect of this operation is that all bits move to the
left a specified number of times. High-order bits are lost. Lower order bits are
zero filled. This operator may be used on the int, uint, long, and ulong data
types. Here's an example.
uint intMax = 4294967295; // 11111111111111111111111111111111b
uint byteMask;
byteMask = intMax << 8; // 11111111111111111111111100000000b
The Right Shift Operator
The right shift operator (>>) shifts the bits of a number to
the right. By providing a number to operate on and the number of digits, every
bit shifts to the right by the number of digits specified. Only use the right
shift operator on int, uint, long, and ulong
data types. The uint, ulong, positive int, and
positive long types shift zeros from the left. The negative
int and negative long types keep a 1 in the sign bit
position and fill the next position to the right with a 0. Here are
some examples:
uint intMax = 4294967295; // 11111111111111111111111111111111b
uint shortMask;
shortMask = intMax >> 16; // 00000000000000001111111111111111b
int intMax = -1; // 11111111111111111111111111111111b
int shortMask;
shortMask = intMax >> 16; // 10000000000000001111111111111111b
For Java Programmers
C# doesn't
have a right shift with zero extension operator (>>>).
Relational Operators
Relational operators are used to make a comparison between two expressions.
The primary difference between relational operators and arithmetic operators is
that relational operators return a bool type rather than a number. Another
difference is that arithmetic operators are applicable to certain C# types
whereas relational operators can be used on every possible C# type, whether
built-in or not. Floating-point types are evaluated according to IEEE 754. The
results of a relational expression are either true or
false.
The Equal Operator
To see if two expressions are the same, use the equal operator (==).
The equal operator works the same for integral, floating-point, decimal, and
enum types. It simply compares the two expressions and returns a bool result.
Here's an example:
bool bresult;
decimal debit = 1500.00m;
decimal credit = 1395.50m;
bresult = debit == credit; // bresult = false
When comparing floating-point types, +0.0 and 0.0 are
considered equal. If either floating-point number is NaN (not a number), equal
returns false.
The Not Equal Operator
The not equal operator (!=) is the opposite of the equal operator
for all types, with a slight variation for floating-point types only. If one of
the floating-point numbers is NAN (not a number), not equal returns true.
There are two forms of not equal applicable to expressions. The first is the
normal not equal operator (!=). The other is a negation of the equal
operator !(a==b). Normally, these two forms always evaluate to the same
value. The exception occurs when evaluating floating-point expressions where one
or both expressions evaluate to NaN and the relational operator in the negation
of an expression is <, >, <=, or
>=. The a > b form evaluates to false, but the
!(a<=b) evaluates to true. Here are some examples:
bool bresult;
decimal debit = 1500.00m;
decimal credit = 1395.50m;
bresult = debit != credit; // bresult = true
bresult = !(debit == credit); // bresult = true
The Less Than Operator
If it's necessary to find out if one value is smaller than another, use
the less than operator (<). The expression on the left is being
evaluated and the expression on the right is the basis of comparison. When the
expression on the left is a lower value than the expression on the right, the
result is true. Otherwise, the result is false. Here's an
example:
short redBeads = 2;
short whiteBeads = 23;
bool bresult;
bresult = redBeads < whiteBeads; // bresult=true, work harder
The Greater Than Operator
If it's necessary to know that a certain value is larger than another,
use the greater than operator (>). It compares the expression on the
left to the basis expression on the right. When the expression on the left is a
higher value than the expression on the right, the result is true.
Otherwise, the result is false. Here's an example:
short redBeads = 13;
short whiteBeads = 12;
bool bresult;
bresult = redBeads > whiteBeads; // bresult=true, good job!
The Less Than or Equal Operator
Sometimes it's necessary to know if a number is either lower than or
equal to another number. That's what the less than or equal operator
(<=) is for. The expression on the left is compared to the expression on the
right. When the expression on the left is either the same value as or less than
the one on the right, less than or equal returns true. This operator is
the opposite of the greater than operator, which means that !(a>b)
would produce the same results. The exception is when there's a
floating-point expression evaluating to NaN, in which case the result is always
true. Here's an example of the less than or equal operator:
float limit = 4.0f;
float currValue = 3.86724f;
bool Bresult;
bresult = currValue <= limit; // bresult = true
The Greater Than or Equal Operator
As its name implies, the greater than or equal operator (>=)
checks a value to see if it's greater than or equal to another. When the
expression to the left of the operator is the same as or more than the
expression on the right, greater than or equal returns true. The
greater than or equal operator is the opposite of the less than operator.
Here's an example:
double rightAngle = 90.0d;
double myAngle = 96.0d;
bool isAbtuse;
isAbtuse = myAngle >= rightAngle; // Yes, myAngle is abtuse
Logical Operators
Logical operators perform Boolean logic on two expressions. There are three
types of logical operators in C#: bitwise, Boolean, and conditional.
The bitwise logical operators perform Boolean logic on corresponding bits of
two integral expressions. Valid integral types are the signed and unsigned int
and long types. They return a compatible integral result with each bit
conforming to the Boolean evaluation.
Boolean logical operators perform Boolean logic upon two Boolean expressions.
The expression on the left is evaluated, and then the expression on the right is
evaluated. Finally, the two expressions are evaluated together in the context of
the Boolean logical operator between them. They return a bool result
corresponding to the type of operator used.
The conditional logical operators operate much the same way as the Boolean
logical operators with one exception in behavior: Once the first expression is
evaluated and found to satisfy the results of the entire expression, the second
expression is not evaluated. This is efficient because it doesn't make
sense to continue evaluating an expression when the result is already known.
The Bitwise AND Operator
The bitwise AND operator (&) compares corresponding bits of two
integrals and returns a result with corresponding bits set to 1 when both
integrals have 1 bits. When either or both integrals have a 0 bit, the
corresponding result bit is 0. Here's an example:
byte oddMask = 1; // 00000001b
byte someByte = 85; // 01010101b
bool isEven;
isEven = (oddMask & someByte) == 0; //(oddMask & someByte) = 1
The Bitwise Inclusive OR Operator
The bitwise inclusive OR operator (|) compares corresponding bits of
two integrals and returns a result with corresponding bits set to 1 if either of
the integrals have 1 bits in that position. When both integrals have a 0 in
corresponding positions, the result is zero in that position. Here's an
example:
byte option1 = 1; // 00000001b
byte option2 = 2; // 00000010b
byte totalOptions;
totalOptions = (byte) (option1 | option2); // 00000011b
The Bitwise Exclusive OR Operator
The bitwise exclusive OR operator (^) compares corresponding bits of
two integrals and returns a result with corresponding bits set to 1 if
only one of the integrals has a 1 bit and the other integral has a
0 bit in that position. When both integral bits are 1 or when
both are 0, the result's corresponding bit is 0.
Here's an example:
byte invertMask = 255; // 11111111b
byte someByte = 240; // 11110000b
byte inverse;
inverse = (byte)(someByte ^ invertMask); //inversion=00001111b
The Boolean AND Operator
The Boolean AND operator (&) evaluates two Boolean expressions
and returns true when both expressions evaluate to true. Otherwise, the
result is false. The result of each expression evaluated must return a
bool result. Here's an example:
bool inStock = false;
decimal price = 18.95m;
bool buy;
buy = inStock & (price < 20.00m); // buy = false
The Boolean Inclusive OR Operator
The Boolean inclusive OR operator (|) evaluates the results of two
Boolean expressions and returns true if either of the expressions
returns true. When both expressions are false, the result of
the Boolean inclusive OR evaluation is false. Both expressions
evaluated must return a bool type value. Here's an example:
int mileage = 2305;
int months = 4;
bool changeOil;
changeOil = mileage > 3000 | months > 3; // changeOil = true
The Boolean Exclusive OR Operator
The Boolean exclusive OR operator (^) evaluates the results of two
Boolean expressions and returns true if only one of the expressions
returns true. When both expressions are true or both
expressions are false, the result of the Boolean exclusive OR
expression is false. In other words, the expressions must be different.
Here's an example:
bool availFlag = false;
bool toggle = true;
bool available;
available = availFlag ^ toggle; // available = true
The Conditional AND Operator
The conditional AND operator (&&) is similar to the Boolean
AND operator in that it evaluates two expressions and returns true when both
expressions are true. It is different when the first expression evaluates to
false. Since both expressions must be true, it's automatically assumed that
if the first expression evaluates to false, the entire expression is false.
Therefore, the conditional AND operator returns false and does not evaluate the
second expression. When the first expression is true, the conditional AND
operator goes ahead and evaluates the second expression. Here's an
example:
bool inStock = false;
decimal price = 18.95m;
bool buy;
buy = inStock && (price < 20.00m); // buy = false
Notice that price < 20 will never be evaluated.
The Conditional OR Operator
The conditional OR operator (||) is similar to the Boolean inclusive
OR operator (|) in that it evaluates two expressions and returns
true when either expression is true. The difference is when
the first expression evaluates to true. Since either expression can be
true to prove that the overall expression is true, the
operator automatically assumes that the entire expression is true when
it finds the first expression is true. Therefore, the conditional OR
operator returns true without evaluating the second expression. When
the first expression is false, the conditional OR operator goes ahead
and evaluates the second expression. Here's an example:
int mileage = 4305;
int months = 4;
bool changeOil;
changeOil = mileage > 3000 || months > 3; // changeOil = true
Notice that because mileage > 3000 is true, months >
3 will never be evaluated.
Side Effects
Watch out for side effects with conditional Boolean operations. Side effects
occur when your program depends on the expression on the right of the
conditional logical operator being evaluated. If the expression on the right is
not evaluated, this could cause a hard-to-find bug. The conditional logical
operators are also called short circuit operators. Take a look at this
example:
decimal totalSpending = 3692.48m;
decimal avgSpending;
bool onBudget = totalSpending > 4000.00m
&& totalSpending < calcAvg();
Notice that the second half of the expression was not evaluated. If
calcAvg() was supposed to change the value of a class field for later
processing, there would be an error.
Warning
When using conditional AND and
conditional OR operators, make sure a program does not depend upon evaluation of
the right-hand side of the expression, because it may not be evaluated. Such
side effects are likely to cause bugs.
Assignment Operators
This chapter has already demonstrated plenty of examples of the simple
assignment operator in action. This section explains the compound operators and
what can be expected from them. Basically, the concept is simple. A compound
operator is a combination of the assignment operator and an arithmetic operator,
bitwise logical operator, or Boolean logical operator. Here's an
example:
int total = 7;
total += 3; // total = 10
This is the same as saying: total = total + 3. Table 3.1 shows a
list of the available compound assignment operators.
Table 3.1 Compound Assignment Operators
|
Operator
|
Function
|
|
*=
|
Multiplication
|
|
/=
|
Division
|
|
%=
|
Remainder
|
|
+=
|
Addition
|
|
-=
|
Subtraction
|
|
<<=
|
Left Shift
|
|
>>=
|
Right Shift
|
|
&=
|
AND
|
|
^=
|
Exclusive OR
|
|
|=
|
Inclusive OR
|
The Ternary Operator
The ternary operator contains three expressions, thus the name
ternary. The first expression must be a Boolean expression. When the
first expression evaluates to true, the value of the second expression
is returned. When the first expression evaluates to false, the value of
the third expression is returned. This is a concise and short method of making a
decision and returning a choice based upon the result of the decision. The
ternary operator is often called the conditional operator. Here's an
example:
long democratVotes = 1753829380624;
long republicanVotes = 1753829380713;
string headline = democratVotes != republicanVotes ?
"We Finally Have a Winner!" : recount();
Other Operators
C# has some operators that can't be categorized as easily as the other
types. These include the is, as, sizeof(),
typeof(), checked(), and unchecked() operators. The
following sections explain each operator.
The is Operator
The is operator checks a variable to see if it's a given type.
If so, it returns true. Otherwise, it returns false.
Here's an example.
int i = 0;
bool isTest = i is int; // isTest = true
The as Operator
The as operator attempts to perform a conversion on a reference
type. The following example tries to convert the integer i into a string. If the
conversion were successful, the object variable obj would hold a
reference to a string object. When the conversion from an as operator
fails, it assigns null to the receiving reference. That's the case
in this example where obj becomes null because i is
an integer, not a string:
int i = 0;
object obj = i as string;
Console.WriteLine("i {0} a string.",
obj == null ? "is not" : "is" ); // i is not a string.
The sizeof() Operator
C# provides a facility to perform low-level functions through a construct
known as unsafe code. The sizeof() operator works only in
unsafe code. The operator takes a type and returns the type's size
in bytes. Here's an example:
unsafe
{
int intSize = sizeof(int); // intSize = 4
}
The typeof() Operator
The typeof() operator returns a Type object. The
Type class holds type information about a value or reference type. The
typeof() operator is used in various places in C# to discover
information about reference and value types. The following example gets type
information on the int type:
Type myType = typeof(int);
Console.WriteLine(
"The int type: {0}", myType ); // The int type: Int32
The checked() Operator
The checked() operator detects overflow conditions in certain
operations. The following example causes a system error by attempting to assign
a value to a short variable that it can't hold:
short val1 = 20000, val2 = 20000;
short myShort = checked((short)(val1 + val2)); // error
The unchecked() Operator
If it is necessary to ignore this error and accept the results regardless of
overflow conditions, use the unchecked() operator as in this
example:
short val1 = 20000, val2 = 20000;
short myShort =
unchecked((short)(val1 + val2)); // error ignored
Tip
Use the /checked[+|-]
command line option when the majority of program code should be checked
(/checked+) or unchecked (/checked-). Then all that needs to
be done inside the code is to annotate the exceptions with the
checked() and unchecked() operators.
Enumeration Expressions
The elements of enumeration expressions evaluate the same as their underlying
types. In addition to using normal operators, there are additional methods that
can be performed with an enum type. An Enum class is used to
obtain the majority of functionality shown in this section. Where the
Enum class is being used, the capitalized Enum class name
prefixes the method call. The examples in this section refer to the following
enum:
enum Weekday { Mon = 1, Tue, Wed, Thu, Fri, Sat = 10, Sun };
For C++ Programmers
C# enums have much
more functionality than C++ enums.
As a typed value, the enum must be assigned to a variable of its type. For
example, the underlying representation of a Weekday enum may default to an
integral value, but it's still a Weekday type. The following line shows the
declaration and initialization of an enum variable:
Weekday w = Weekday.Mon;
During a Console.WriteLine() method call, enum values are printed
with their names rather than their underlying integral values. Here's an
example:
Console.WriteLine("WeekDay: {0}", w); // WeekDay: Mon
The Format() method returns the string representation of an enum
value, as shown here:
Console.WriteLine("Format: {0}", w.Format()); // Format: Mon
To go in the opposite direction and convert a string to an enum, use the
FromString() method. The arguments it accepts are the enum type, the
string representation of the value to be converted, and a Boolean condition to
verify case. The following example uses the typeof() operator to get
the enum type. The string to be converted is Tue, and the method is
case-sensitive.
Console.WriteLine("FromString: {0}",
Enum.FromString(typeof(EnumTest.Weekday),
"Tue", true)); // FromString: Tue
To get the name of an enum variable, use the GetName() method. The
following example shows the GetName() method accepting the enum type
and an instance of that enum type and returning its name as a string.
w = EnumTest.Weekday.Wed;
Console.WriteLine("GetName: {0}",
Enum.GetName(typeof(EnumTest.Weekday), w)); // GetName: Wed
If there is a need to get the string representations of all the members of an
enum, use the GetNames() methodplural of the previous method. The
following example shows an array being filled with the names. The method call
only needs the enum type.
string[] weekDays = new string[7];
weekDays = Enum.GetNames(typeof(EnumTest.Weekday));
Console.WriteLine("Day 1: {0}", weekDays[0]); // Day 1: Mon
Console.WriteLine("Day 2: {0}", weekDays[1]); // Day 2: Tue
Console.WriteLine("Day 3: {0}", weekDays[2]); // Day 3: Wed
Console.WriteLine("Day 4: {0}", weekDays[3]); // Day 4: Thu
Console.WriteLine("Day 5: {0}", weekDays[4]); // Day 5: Fri
Console.WriteLine("Day 6: {0}", weekDays[5]); // Day 6: Sat
Console.WriteLine("Day 7: {0}", weekDays[6]); // Day 7: Sun
A corresponding method to get the values of an enum is the
GetValues() method. The following example shows the
GetValues() method accepting an enum type and returning an array of
objects. Notice that the array is of type objects. In C#, all types are also
object types. Therefore, any type can be assigned to the object type.
object[] weekDayVals = new object[7];
weekDayVals = Enum.GetValues(typeof(EnumTest.Weekday));
Console.WriteLine("Day 1: {0}", weekDayVals[0]); // Day 1: Mon
Console.WriteLine("Day 2: {0}", weekDayVals[1]); // Day 2: Tue
Console.WriteLine("Day 3: {0}", weekDayVals[2]); // Day 3: Wed
Console.WriteLine("Day 4: {0}", weekDayVals[3]); // Day 4: Thu
Console.WriteLine("Day 5: {0}", weekDayVals[4]); // Day 5: Fri
Console.WriteLine("Day 6: {0}", weekDayVals[5]); // Day 6: Sat
Console.WriteLine("Day 7: {0}", weekDayVals[6]); // Day 7: Sun
To find out the underlying type of an enum, use the
GetUnderlyingType() method. It accepts an enum type argument, and the
return value is the integral type of the enum's underlying type.
Here's an example:
Console.WriteLine("Underlying Type: {0}",
Enum.GetUnderlyingType(
typeof(EnumTest.Weekday))); // Underlying Type: Int32
When it's necessary to determine if an enum value is defined, use the
IsDefined() method. It accepts an enum type and an enum value and
returns a Boolean true if the value is defined in the enum. Otherwise, it
returns false. Here's an example:
w = EnumTest.Weekday.Thu;
Console.WriteLine("Thu is Defined: {0}",
Enum.IsDefined(typeof(EnumTest.Weekday), w));
// Thu is Defined: True
To obtain an enum type that is set to a specific value, use the
ToObject() method. The following example shows the method accepting an
enum type and an integer, and returning an enum of the requested type with the
value corresponding to the integer.
Console.WriteLine("Get Friday: {0}",
Enum.ToObject(typeof(EnumTest.Weekday), 5));
// Get Friday: Fri
Array Expressions
Besides being an efficient storage construct, arrays have additional
functionality that helps make programs more expressive and powerful. The
following example shows one such capability:
string[] weekDays = new string[7];
Console.WriteLine("Number of Days: {0}",
weekDays.Length); // Number of Days: 7
For C++ Programmers
From the perspective of traditional built-into-the-language arrays, C++ arrays
are simply a pointer to a block of memory. This refers to the C++ arrays derived
from its C language ancestry. C# arrays have much more functionality.
The C++ STL array class is similar to the C# ArrayList
collection class. Both are library classes.
The previous example showed the array's Length property. The array type
has many more methods and properties, as shown in Table 3.2.
Table 3.2 C# Array Members
|
Method/Property
|
Description
|
|
AsList
|
Returns an Ilist representation of the array
|
|
BinarySearch
|
Finds a value in a one-dimensional array using a binary search
|
|
Clear
|
Cleans out a range of array values by setting them to 0 or null
|
|
Copy
|
Copies a range of array elements to another array
|
|
CreateInstance
|
Creates a new instance of an array
|
|
IndexOf
|
Finds the first occurrence of a value and returns its index
|
|
LastIndexOf
|
Finds the last occurrence of a value and returns its index
|
|
Reverse
|
Reverses the elements of a one-dimensional array
|
|
Sort
|
Sorts a one-dimensional array
|
|
IsReadOnly
|
Returns true if read-only
|
|
IsSynchronized
|
Returns true if synchronized
|
|
Length
|
Returns the number of elements in all dimensions
|
|
Rank
|
Returns the number of dimensions
|
|
SyncRoot
|
Returns array synchronization object
|
|
Clone
|
Performs a shallow copy
|
|
CopyTo
|
Copies from one array to another
|
|
Equals
|
Compares array references for equality
|
|
GetEnumerator
|
Returns an IEnumerator of a one-dimensional array
|
|
GetHashCode
|
Returns a unique identifier
|
|
GetLength
|
Returns the number of elements in specified dimension
|
|
GetLowerBound
|
Returns the lower bound of a dimension
|
|
GetType
|
Returns the Type object
|
|
GetUpperBound
|
Returns the upper bound of a dimension
|
|
GetValue
|
Returns values from specified elements
|
|
Initialize
|
Calls the default constructor of each element
|
|
SetValue
|
Sets values of specified elements
|
|
ToString
|
Returns a string representation
|
Statements
Statements in C# are single entities that cause a change in the
program's current state. They're commonly associated with some type of
assignment statement, changing the value of a variable. A statement ends with a
semicolon (;). Leave one out and the compiler will issue a prompt notification.
Statements may span multiple lines, which could help make your code more
readable, as the following example shows:
decimal closingCosts = loanOrigination
+ appraisal
+ titleSearch
+ insuranceAdvance
+ taxAdvance
+ points
+ realtorCommission
+ whateverElseTheyCanRipYouOffFor;
Had the statement been placed on one line, it would have either continued off
the right side of the page or wrapped around in an inconvenient location. This
way, each item is visible, lined up nicely, and easier to understand.
Blocks
Setting off code in blocks clearly delimits the beginning and ending of a
unit of work and establishes scope. Begin a block of code with a left-hand brace
({), and end it with a right-hand brace (}). Blocks are
required to specify the boundaries of many language elements such as classes,
interfaces, structures, properties, indexers, events, and methods.
Labels
Labels are program elements that simply identify a location in a program.
Their only practical use is to support the goto statement. The
goto statement allows program control to jump to the place where a
label is defined. A label is any valid identifier followed by a colon (not a
semicolon). Here are two examples:
loop: // a label named "loop"
jumphere: // a label named "jumphere"
Declarations
Declarations enable definition and announcement of the existence and nature
of program data. There are two forms of declaration in C#: simple declaration
and declaration with initialization. A simple declaration takes the following
form:
<type> <identifier>;
The type may be any C# or user-defined type. The identifier
is any valid identifier as defined in Chapter 2, "Getting Started with
C#."
A declaration with initialization looks like this:
<type> <identifier> = <expression>;
The type and identifier are the same as the previous
example. The equal sign takes the evaluated expression on its right and loads it
into the variable declared on the left. The expression can be any valid C#
statement evaluating to the type of variable specified by type. The declaration
is a statement followed by a semicolon.
Operator Precedence and Associativity
When evaluating C# expressions, there are certain rules to ensure the outcome
of the evaluation. These rules are governed by precedence and associativity and
preserve the semantics of all C# expressions. Precedence refers to the order in
which operations should be evaluated. Sub-expressions with higher operator
precedence are evaluated first.
There are two types of associativity: left and right. Operators with left
associativity are evaluated from left to right. When an operator has right
associativity, its expression is evaluated from right to left. For example, the
assignment operator is right associative. Therefore, the expression to its right
is evaluated before the assignment operation is invoked. Table 3.3 shows the C#
operators, their precedence, and associativity.
Certain operators have precedence over others to guarantee the certainty and
integrity of computations. One effective rule of thumb when using most operators
is to remember their algebraic precedence. Here's an example:
int result;
result = 5 + 3 * 9; // result = 32
This computes 3 * 9 = 27 + 5 = 32. To alter the order of operations
use parentheses, which have a higher precedence:
result = (5 + 3) * 9; // result = 72
This time, 5 and 3 were added to get 8 and then multiplied by 9 to get 72.
See Table 3.3 for a listing of operator precedence and associativity. Operators
in top rows have precedence over operators in lower rows. Operators on the left
in each row have higher precedence over operators to the right in the same
row.
Table 3.3 Operator Precedence and Associativity
|
Operators
|
Associativity
|
|
(x), x.y, f(x), a[x], x++,
x--, new,
|
Left
|
|
typeof, sizeof, checked, unchecked
|
|
|
+ (unary), (unary), ~, ++x,
--x, (T)x
|
Left
|
|
*, / %
|
Left
|
|
+ (arithmetic), (arithmetic)
|
Left
|
|
<<, >>
|
Left
|
|
<, >, <=, >=, is,
as
|
Left
|
|
==, !=
|
Left
|
|
&
|
Left
|
|
^
|
Left
|
|
|
|
Left
|
|
&&
|
Left
|
|
||
|
Left
|
|
?:
|
Right
|
|
=, *=, /=, %=, +=, -=,
<<=, >>=, &=, ^=,
|=
|
Right
|
Summary
This chapter covered the various C# operatorsunary, arithmetic,
relational operators, and other operatorsand provided examples of how to
use them.
The unary operators include plus, minus, increment, decrement, logical
complement, and bitwise complement operators. Binary operators include the
arithmetic, logical, relational and assignment operators. There is a single
ternary operator that produces conditional results. C# has a few other operators
that don't fit into the any of those categories; they include the
is, as, typeof, sizeof, checked,
and unchecked operators.
The enum and array types have additional functions that make programs more
expressive and powerful. I included several examples of enums and a table of
array methods and properties.
This chapter also described statements, blocks, labels, and declarations, and
included a section about operator precedence and associativity.
Having mastered the material in this chapter, it's simple to move into
logical manipulation of program flow.
© Copyright Pearson Education. All rights reserved.