#98 – Using an Indexer to Get a Specific Character In a String

In C#, you can use the indexer operator [ ], to get a specified character in a string.

The indexer takes a zero-based integer as an index into the string.  0 returns the first character and n-1 (where n is the length of the string) returns the last character.

 string s = "ABCDE";
 char c = s[0];   // A
 c = s[2];        // C (3rd char)
 c = s[4];        // E

Using a negative value for the index will result in an IndexOutOfRangeException being thrown.

Note that indexers work to extract Unicode characters only if they are 2-byte UTF16 characters.  The indexer cannot retrieve a 4-byte surrogate pair.

 string s = "A€C";
 char c = s[1];         // Works: €

 s = "A𠈓C";
 c = s[1];       // Doesn't work: unprintable character

#97 – String Comparisons Using Other Cultures

Each language has different rules for how to sort strings, based on the alphabetical ordering of the characters used for that language.  In .NET, information about a language/place combination is captured in an instance of the CultureInfo class.  In turn, the CultureInfo object has a CompareInfo property that points to an instance of the CompareInfo class, defining rules for sorting strings in that culture.

By default, when you use the String.Compare method, the sorting rules for the current culture (CultureInfo.CurrentCulture) are used.  But you can override this by adding a CultureInfo parameter on the Compare method.

You can pass a new instance of CultureInfo by creating an instance using a unique culture code.  The culture code indicates language and region.

 int n;
 n = string.Compare("åb", "bb", true);  // -1 (å < b in English)
 n = string.Compare("åb", "bb", true, new CultureInfo("nn-NO"));    // 1  (å > b in Norwegian)

#96 – Comparing String Values

The < and > operators are not overloaded for the System.String type, which means that you can’t compare strings using the relational operators.

Instead. you can use the static System.String.Compare method, which takes two strings and returns an integer value.  If the first string is less than the second, a negative number is returned.  If the first string is greater, a positive number is returned.  If the strings are equal, the return value is zero.

 int n = string.Compare("Sean", "Steinbeck");    // -1
 n = string.Compare("Sean", "Bozo");             // 1
 n = string.Compare("Sean", "Sean");             // 0
 n = string.Compare("Sean", "sean");             // 1 ("S" > "s")

You can also use the CompareTo instance method of the string type:

 int n = "Sean".CompareTo("Giotto");   // 1 (Sean > Giotto)

You can also ignore case during the comparison:

 n = string.Compare("Sean", "sean", true);         // 0

#95 – ToString() Called Automatically When Doing String Concatenation

When doing string concatenation, either using the + operator or when using the String.Concat or String.Format methods, you can concatenate objects that are not strings.  .NET will attempt to convert these objects to strings before doing the concatenation by calling the object’s ToString method.

Here’s an example:

string s1 = "Ten: " + 10;   // Ten: 10

This is equivalent to:

 int n = 10;
 string s1 = "Ten: " + n.ToString();

This works for any object:

 DateTime dt = DateTime.Now;
 string s2 = "Date and time: " + dt;

This causes the DateTime.ToString method to be called, which results in a string that looks like:

Date and time: 9/16/2010 4:01:44 PM

#94 – Converting Between Char and Numeric Types

You can implicitly convert from char to any numeric type that can store values in the range [0,65535].  This means that you can convert to int implicitly, as well as ushort, but not to short.

 char c1 = 'X';
 int n1 = c1;     // Implicit conversion to numeric
 ushort s1 = c1;  // Also works
 short s2 = c1;   // Implicit not allowed, must cast
 byte b = c1;     // Implicit not allowed, must cast

You can also convert from a numeric type to a char, but no implicit conversion exists–you must always use a cast or the Convert class.

 short n2 = 90;
 char c2 = (char)n2;  // No implicit conversion, must use cast

#93 – Escape Sequences in Character Literals

Just as we can include escape sequences in string literals, we can also use escape sequences in character literals to indicate special or non-printable characters.

The list of allowed escape sequences for character literals in C# is the same as for string literals, with the exception of the 8-byte Unicode literal for surrogate pairs:

  • \a  –  Bell (alert)
  • \b  –  Backspace
  • \f  –  Formfeed
  • \n  –  New line
  • \r  –  Carriage return
  • \t  –  Horizontal tab
  • \v  –  Vertical tab
  • \’  –  Single quote
  • \”  –  Double quote
  • \\  –  Backslash
  • (backslash followed by 0) – Null
  • \xhh  –  ASCII character in hex
  • \xhhhh  –  Unicode character in hex
  • \uhhhh – Unicode character  (4-byte)

Here are some examples in code:

 char c4 = '\n';    // Newline
 char c5 = '\r';    // Carriage return
 char c6 = '\t';    // Tab
 char c8 = '\'';    // Single quote
 char c9 = '\"';    // Double quote
 char c10 = '\\';   // Backslash
 char c11 = '\0';   // Null
 char c12 = '\x2E';  // hex
 char c13 = '\xe213';  // hex

#92 – The Ternary Conditional Operator

The conditional operator (?:) take three operands.  The first is a boolean expression to evaluate. If this expression is true, the value of the conditional operator is equal to the second operand.  If it’s false, the value of the conditional operator is equal to the third operand.

So for the expression

 a ? b : c

the value of this expression is b if a is true, or c if a is false.

Here’s an example of using the conditional operator (assume that isRaining is a boolean):

string describeWeather = isRaining ? "Wet" : "Nice";

A conditional operator can always be rewritten as an if statement.  The previous example is equivalent to:

string describeWeather;
if (isRaining)
    describeWeather = "Wet";
else
    describeWeather = "Nice";

Using the conditional operator can lead to more concise code.