#65 – Verbatim String Literals

Because the backslash (\) is the first character in an escape sequence, you need to use the double-backslash sequence (\\) to embed actual backslashes in a string literal.

 string file = "C:\\MyDir\\Another Dir\\thefile.txt";

Because this can get a little hard to read, C# allows using the at sign (@) character to indicate a verbatim string literal–a string literal in which escape sequences should not be interpreted.

Using a verbatim string literal, we can write the earlier string without doubling the backslashes:

 string file = @"C:\MyDir\Another Dir\thefile.txt";

We can also use a verbatim string literal to split a string across multiple lines in the source code, rather than embedding the \n escape sequence:

string file = @"First line
Second line
Third line";

#64 – Escape Sequences in String Literals

C# allows embedding special (often non-printable) characters into a string literal using an escape sequence.  An escape sequence is a series of characters that begins with a backslash (\), followed by one or more letters or digits.

Here’s an example of embedding several newline characters into a string, so that it’s printed on three different lines.

 Console.Write("First line\nSecond line\nThird line\n");     // 3 lines

Full list of escape sequences in C#:

  • \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)
  • \Uhhhhhhhh – Unicode surrogate pair (8-byte)

#63 – Use StringBuilder for More Efficient String Concatentation

Using the concatenation operator (+) for string concatenation is convenient, but can be inefficient because a new string is allocated for each concatenation.

Let’s say that we do a simple test, appending the string equivalents of the first 50,000 integers:

string s1 = "";
for (int i = 0; i < 50000; i++)
    s1 = s1 + i.ToString();

In one test environment, this loop took 30,430 milliseconds.

We can make this code more efficient by using the Append method of the StringBuilder class, which avoids allocating memory for the string on each iteration:

 StringBuilder sb = new StringBuilder("");
 for (int i = 0; i < 50000; i++)

In the same test environment as before, this version takes 6 milliseconds.

StringBuilder is definitely more efficient, but likely worth using only when you plan on doing a large number of string concatenations.  See also Concatenating Strings Efficiently.

#62 – String Concatenation

In C#, you can use the ‘+’ (plus) character to concatenate multiple strings together.

 string s1 = "C#";
 string s2 = "fun";
 string s3 = s1 + " is " + s2;   // "C# is fun"

You can use one or more concatenation operators in the same expression and you can use an expression containing the concatenation operator anywhere that a string value is expected.

You can also use String.Concat or String.Format to concatenate strings.

 // Concat method
 string s4 = String.Concat(new object[] { "The ", 3, " musketeers" });
 string s5 = String.Concat("This", "That");    // ThisThat

 // Use String.Format to concatenate
 string s6 = string.Format("{0}{1}{2}", s1, " is ", s2);

#61 – Formatting and Parsing Strings for Non-Default Cultures

By default, when you convert a numeric data value to/from a string, the current culture is used.

The current culture dictates what characters to use for the thousands separator, decimal symbol, and the default formatting for date and time strings.  You can change the culture from the Formats tab in the Region and Language dialog in Control Panel.  (Windows 7).

Current culture typically matches the region that a user is located in.  It’s different from “UI culture”, which is the culture for the native language of the operating system.

You typically format output strings in the current culture and parse input strings based on the current culture.  But you can override this behavior and specify a culture to use.

 float f1 = float.Parse("4.5");   // Fails in regions that use ',' for decimal (e.g. France)
 float f2 = float.Parse("4.5", CultureInfo.InvariantCulture);    // Assume invariant (English), e.g. '.' for decimal

 // Display date/time using German culture
 Console.WriteLine(DateTime.Now.ToString(new CultureInfo("de-DE")));

#60 – Using Parse to Convert from String to Numeric Types

Each of the numeric types in C# has a Parse method that you can use when converting from string representations of a numeric value to an object of the appropriate numeric type.

Here are some examples:

 byte b1 = byte.Parse("200");
 sbyte sb1 = sbyte.Parse("-100");
 float f1 = float.Parse("1.2e-4");

If the string does not represent a value of the associated numeric type, or represents a numeric value that is outside the range of the type, a FormatException or OverflowException is generated.

 int n1 = int.Parse("3.4");    // FormatException
 uint ui1 = uint.Parse("-1");  // OverflowException

Numeric strings containing digit grouping (thousand separator) or decimal points are assumed to be in a format matching the current culture.  E.g. ‘.’ for decimal symbol in the US, ‘,’ for decimal symbol in France.

#59 – Using Unchecked Keyword to Avoid Overflow Exceptions

If the default project setting is to check for arithmetic overflow/underflow, you can temporarily avoid checking using the unchecked keyword.  This reverts to the default behavior of wrapping the result value.

Assume that we’ve changed the project setting so that we do check for arithmetic overflow.  We can then avoid an exception by using the unchecked keyword on an expression:

 int n1 = int.MaxValue;   // 2147483647 (0x7FFFFFFF)
 int n2 = unchecked(n1 + 1);  // Wraps: -2147483648
 int n3 = n1 + 1;             // Throws OverflowException--default project setting

We can also use the unchecked keyword on an entire statement block:

 int n1 = int.MaxValue;   // 2147483647 (0x7FFFFFFF)
     int n2 = n1 + 1;  // Wraps: -2147483648
     int n4 = n1 * 2;  // Wraps: -2

You can also use unchecked to avoid overflow checking on constant expressions at compile-time.  This results in no compile-time error and no overflow exception at run-time.

 int n1 = int.MaxValue + 1;  // Compile-time error: overflow
 int n2 = unchecked(int.MaxValue + 1);

#58 – Generate Exceptions on Integer Overflow Using Checked Operator

By default, when an overflow occurs as a result of an arithmetic operation on an integer type, the result wraps.

 int n1 = int.MaxValue;      // 2147483647 (0x7FFFFFFF)
 n1 = n1 + 1;                // Now -2147483648 (wrapped)

You can instead cause an exception to be thrown whenever an integer overflow condition occurs. To do this, use the checked keyword on the expression:

 int n1 = int.MaxValue;      // 2147483647 (0x7FFFFFFF)
 n1 = checked(n1 + 1);       // Throws OverflowException

You can also use the checked keyword on the statement block that contains the expression:

     int n1 = int.MaxValue;   // 2147483647 (0x7FFFFFFF)
     n1 = n1 + 1;             // Throws OverflowException

You can also make a change to your project so that the default behavior is to throw the exception, even without the checked keyword.  Go to Project Properties, click on Build tab, then click on the Advanced button at the bottom of the window.  This brings up the Advanced Build Settings dialog.

#57 – Overflow on Integer Operations

When executing arithmetic operations on integer-based types, an overflow can occur when the result of the operation cannot be stored in the destination type.

By default in C#.NET, when overflow occurs for integer-based types, the most significant bits of the result are discarded.  Effectively, this results in the value appearing to wrap.  E.g. Large values wrap to smaller (or large negative for unsigned) values.

Here are some examples of wrapping on integer-based overflow.

 uint u1 = 0xffffffff;
 u1 = u1 + 5;       // Now 0x00000004 (wrapped)

 int n1 = int.MaxValue;      // 2147483647 (0x7FFFFFFF)
 n1 = n1 + 1;                // Now -2147483648 (wrapped)

 short s1 = short.MinValue;
 s1 = (short)(s1 - 1);       // Now 32767 (wrapped)

#56 – How to Round When Converting Float to Int

When converting from float or double to int, we cannot use an implicit cast, because data would be lost.  We need an explicit cast, as shown below.  When casting from float or double to int, the resulting value is truncated (rounded towards 0) to get an integral result.

 float f1 = 4.8f;
 //int n1 = f1;        // Compile-time error: cannot implicitly convert
 int n1 = (int)f1;     // Explicit cast, truncated, n1 = 4

If you want to round to the nearest integer, rather than truncating, you can use one of the methods in System.Convert.

 n1 = Convert.ToInt32(4.8f);   // Rounded, n1 = 5

The Convert method always rounds to the nearest integer, unless the result is halfway between two integers.  In this case, it rounds to the nearest even integer.

 n1 = Convert.ToInt32(8.5f);  // Rounded to nearest even, n1 = 8
 n1 = Convert.ToInt32(9.5f);  // Rounded to nearest even, n1 = 10