Friday, July 3, 2015

C# in Nutshell Chapter 6 - Framework Fundamentals


The static string.IsNullOrEmpty method is a useful shortcut for testing whether a given string is either null or empty.

string empty = string.Empty
"abc".IndexOf("a")  // return 0;
"abcdef".StartsWith ("abc", StringComparison.InvariantCultureIgnoreCase)
"abcde".IndexOf ("CD", StringComparison.CurrentCultureIgnoreCase); // 2

LastIndexOf is like IndexOf, but works backward through the string.

IndexOfAny returns the first matching position of any one of a set of characters

Console.Write ("ab,cd ef".IndexOfAny (new char[] {' ', ','} )); // 2
Console.Write ("pas5w0rd".IndexOfAny ("0123456789".ToCharArray() )); // 3

LastIndexOfAny does the same in the reverse direction.

Manipulating strings

string left3 = "12345".Substring (0, 3); // left3 = "123";
string mid3 = "12345".Substring (1, 3); // mid3 = "234";
string end3 = "12345".Substring (2); // end3 = "345";

string s1 = "helloworld".Insert (5, ", "); // s1 = "hello, world"
string s2 = s1.Remove (5, 2); // s2 = "helloworld";

PadLeft and PadRight pad a string to a given length with a specified character (or a space if unspecified). If the input string is longer than the padding length, the original string is returned

Console.WriteLine ("12345".PadLeft (9, '*')); // ****12345
Console.WriteLine ("12345".PadLeft (9)); // 12345

TrimStart and TrimEnd remove specified characters from the beginning or end of a string; Trim does both. By default, these functions remove whitespace characters (including spaces, tabs, new lines, and Unicode variations of these): 

Console.WriteLine (" abc \t\r\n ".Trim().Length); // 3

Replace replaces all (non-overlapping) occurrences of a particular character or substring:

Console.WriteLine ("to be done".Replace (" ", " | ") ); // to | be | done
Console.WriteLine ("to be done".Replace (" ", "") ); // tobedone

ToUpper() ToLower() ToUpperInvariant() ToLowerInvariant()

string[] words = "The quick brown fox".Split();
string together = string.Join (" ", words); // The static Join method does the reverse of Split

The static Concat method is similar to Join but accepts only a params string array and applies no separator. Concat is exactly equivalent to the + operator.

string sentence = string.Concat ("The", " quick", " brown", " fox");
string sameSentence = "The" + " quick" + " brown" + " fox";


string composite = "It's {0} degrees in {1} on this {2} morning";
string s = string.Format (composite, 35, "Perth", DateTime.Now.DayOfWeek);
// s == "It's 35 degrees in Perth on this Friday morning"

Each number in curly braces is called a format item. The number corresponds to the argument position and is optionally followed by:
  • A comma and a minimum width to apply
  • A colon and a format string
The minimum width is useful for aligning columns. If the value is negative, the data is left-aligned; otherwise, it’s right-aligned. For example:

string composite = "Name={0,-20} Credit Limit={1,15:C}";
Console.WriteLine (string.Format (composite, "Mary", 500));
Console.WriteLine (string.Format (composite, "Elizabeth", 20000));

The equivalent without using string.Format is this:
string s = "Name=" + "Mary".PadRight (20) + " Credit Limit=" + 500.ToString ("C").PadLeft (15);

Comparing Strings

There are two basic algorithms for string comparison: ordinal and culture-sensitive. Ordinal comparisons interpret characters simply as numbers (according to their numeric Unicode value); culture-sensitive comparisons interpret characters with reference to a particular alphabet. There are two special cultures: the “current culture,” which is based on settings picked up from the computer’s control panel, and the “invariant culture,” which is the same on every computer (and closely matches
American culture).

Despite ordinal’s limitations, string’s == operator always performs ordinal case sensitive comparison.The same goes for the instance version of string.Equals when called without arguments; this defines the “default” equality comparison behavior for the string type.

The following methods allow culture-aware or case-insensitive comparisons:
  • public bool Equals(string value, StringComparison comparisonType);
  • public static bool Equals (string a, string b, StringComparison comparisonType);

The static version is advantageous in that it still works if one or both of the strings are null. StringComparison is an enum defined as follows:

public enum StringComparison
CurrentCulture, // Case-sensitive
InvariantCulture, // Case-sensitive
Ordinal, // Case-sensitive

String order comparison

String’s CompareTo instance method performs culture-sensitive, case-sensitive order comparison. Unlike the == operator, CompareTo does not use ordinal comparison: for ordering, a culture-sensitive algorithm is much more useful. Here’s the method’s definition:

public int CompareTo (string strB);

For other kinds of comparison, you can call the static Compare and CompareOrdinal
  1. public static int Compare (string strA, string strB, StringComparison comparisonType);
  2. public static int Compare (string strA, string strB, bool ignoreCase, CultureInfo culture);
  3. public static int Compare (string strA, string strB, bool ignoreCase);
  4. public static int CompareOrdinal (string strA, string strB);

Console.WriteLine ("Boston".CompareTo ("Austin")); // 1
Console.WriteLine ("Boston".CompareTo ("Boston")); // 0
Console.WriteLine ("Boston".CompareTo ("Chicago")); // −1
Console.WriteLine ("ṻ".CompareTo ("ǖ")); // 0
Console.WriteLine ("foo".CompareTo ("FOO")); // −1

By supplying a CultureInfo object, you can plug in any alphabet:
// CultureInfo is defined in the System.Globalization namespace

CultureInfo german = CultureInfo.GetCultureInfo ("de-DE");
int i = string.Compare ("Müller", "Muller", false, german);


The StringBuilder class (System.Text namespace) represents a mutable (editable) string. With a StringBuilder, you can Append, Insert, Remove, and Replace substrings without replacing the whole StringBuilder.

StringBuilder’s constructor optionally accepts an initial string value, as well as a starting size for its internal capacity (default is 16 characters). If you go above this, StringBuilder automatically resizes its internal structures to accommodate (at a slight performance cost) up to its maximum capacity (default is int.MaxValue). A popular use of StringBuilder is to build up a long string by repeatedly calling Append.

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 50; i++) sb.Append (i + ",");
Console.WriteLine (sb.ToString())

In our example above, the expression i + "," means that we’re still repeatedly concatenating strings. However, this incurs only a small performance cost in that the strings in question are small
and don’t grow with each loop iteration. For maximum performance, however, we could change the loop body to this: { sb.Append (i.ToString()); sb.Append (","); }

Methods: AppendLine, AppendFormat, Remove, Insert, Replace

Encoding object

Encoding utf8 = Encoding.GetEncoding ("utf-8");

Encoding chinese = Encoding.GetEncoding ("GB18030");

The most common encodings can also be obtained through dedicated static prop-
erties on Encoding:

  1. Encoding.UTF8
  2. Encoding.Unicode
  3. Encoding.UTF32
  4. Encoding.ASCII

foreach (EncodingInfo info in Encoding.GetEncodings())
    Console.WriteLine (info.Name);

Dates and Times


A TimeSpan represents an interval of time—or a time of the day


  1. public TimeSpan (int hours, int minutes, int seconds);
  2. public TimeSpan (int days, int hours, int minutes, int seconds);
  3. public TimeSpan (int days, int hours, int minutes, int seconds, int milliseconds);
  4. public TimeSpan (long ticks);  // Each tick = 100ns
Or more convenient to use static From... method:

  • public static TimeSpan FromDays (double value);
  • public static TimeSpan FromHours (double value);
  • public static TimeSpan FromMinutes (double value);
  • public static TimeSpan FromSeconds (double value);
  • public static TimeSpan FromMilliseconds (double value);
For example:
Console.WriteLine (new TimeSpan (2, 30, 0));   // 02:30:00
Console.WriteLine (TimeSpan.FromHours (2.5));  // 02:30:00
Console.WriteLine (TimeSpan.FromHours (−2.5)); // −02:30:00

TimeSpan overloads the + and - operators:

TimeSpan.FromHours(2) + TimeSpan.FromMinutes(30);
TimeSpan.FromDays(10) - TimeSpan.FromSeconds(1);  // 9.23:59:59

TimeSpan can also be used to represent the time of the day (the elapsed time since midnight). To obtain the current time of day, call DateTime.Now.TimeOfDay .

DateTime and DateTimeOffset

A DateTime incorporates a three-state flag indicating whether the DateTime is relative to:
  • The local time on the current computer
  • UTC (the modern equivalent of Greenwich Mean Time)
  • Unspecified
A DateTimeOffset is more specific—it stores the offset from UTC as a TimeSpan:

July 01 2007 03:00:00 −06:00

This influences equality comparisons, which is the main factor in choosing between DateTime and DateTimeOffset. Specifically:
  • DateTime ignores the three-state flag in comparisons and considers two values equal if they have the same year, month, day, hour, minute, and so on.
  • DateTimeOffset considers two values equal if they refer to the same point in time.
DateTime dt1 = new DateTime (2000, 1, 1, 10, 20, 30, DateTimeKind.Local);
DateTime dt2 = new DateTime (2000, 1, 1, 10, 20, 30, DateTimeKind.Utc);
Console.WriteLine (dt1 == dt2);
DateTime local = DateTime.Now; // True
DateTime utc = local.ToUniversalTime();
Console.WriteLine (local == utc); // False

DateTime considers the following two values different, whereas DateTimeOffset
considers them equal:
July 01 2007 09:00:00 +00:00 (GMT)
July 01 2007 03:00:00 −06:00 (local time, Central America)

Convert from DateTime to DateTimeOffset

You can construct a DateTimeOffset from an existing DateTime either by using these

public DateTimeOffset (DateTime dateTime);
public DateTimeOffset (DateTime dateTime, TimeSpan offset);

or with an implicit cast. The implicit cast from DateTime to DateTimeOffset is handy because most of the .NET Framework supports DateTime—not DateTimeOffset.

DateTimeOffset dt = new DateTime (2000, 2, 3);

If you don’t specify an offset, it’s inferred from the DateTime value using these rules:

  • If the DateTime has a DateTimeKind of Utc, the offset is zero.
  • If the DateTime has a DateTimeKind of Local or Unspecified (the default), the offset is taken from the current local time zone.

Convert from DateTimeOffset to DateTime

DateTimeOffset provides three properties that return values of type DateTime :

  1. The UtcDateTime property returns a DateTime in UTC time.
  2. The LocalDateTime property returns a DateTime in the current local time zone (converting it if necessary).
  3. The DateTime property returns a DateTime in whatever zone it was specified, with a Kind of Unspecified (i.e., it returns the UTC time plus the offset).

TimeZone and TimeZoneInfo

TimeZoneInfo was introduced in Framework 3.5. 
The biggest difference between the two types is that TimeZone lets you access only the current local time zone, whereas TimeZoneInfo provides access to all the world’s time zones. Further, TimeZoneInfo exposes a richer (although at times, more awkward) rules-based model for describing daylight saving time.


Framework 4.0 introduced a new set of generic classes for holding a set of differently
typed elements. These are called tuples.

You can instantiate a tuple either via its constructor or via the static helper method Tuple.Create:

var t = new Tuple<int,string> (123, "Hello");
Tuple<int,string> t = Tuple.Create (123, "Hello");

You can then access the properties as follows (notice that each is statically typed):

Console.WriteLine (t.Item1 * 2);  // 246
Console.WriteLine (t.Item2.ToUpper());  // HELLO

Tuples are convenient in returning more than one value from a method—or creating
collections of value pairs (we’ll cover collections in the following chapter).

An alternative to tuples is to use an object array. However, you then lose static type
safety, incur the cost of boxing/unboxing for value types, and require clumsy casts
that cannot be validated by the compiler:

object[] items = { 123, "Hello" };
Console.WriteLine ( ((int) items[0]) * 2) ;
Console.WriteLine ( ((string) items[1]).ToUpper() );

Comparing Tuples:

var t1 = Tuple.Create (123, "Hello");
var t2 = Tuple.Create (123, "Hello");
Console.WriteLine (t1 == t2);  // False
Console.WriteLine (t1.Equals (t2));  // True


The static Guid.NewGuid method generates a unique Guid:

Guid g = Guid.NewGuid ();
Console.WriteLine (g.ToString());  // 0d57629c-7d6e-4847-97cb-9e2fc25083fe

To instantiate an existing value, you use one of the constructors. The two most useful constructors are:

public Guid (byte[] b);  // Accepts a 16-byte array
public Guid (string g);  // Accepts a formatted string

Being a struct, a Guid honors value-type semantics; hence, the equality operator works.
The static Guid.Empty property returns an empty Guid (all zeros). This is often used in place of null .

Equality Comparison

There are three standard protocols that types can implement for equality comparison:

  1. The == and != operators
  2. The virtual Equals method in object
  3. The IEquatable<T> interface

Because object is a class (and so a reference type), object’s == operator uses referential equality to compare x and y. The result is false, because x and y each refer to different boxed objects on the heap. "==" and "!=" are operators, i.e. static and resolved at compiling time.

int x = 5, y = 5;

Console.WriteLine (x == y); // True

object x = 5, y = 5;
Console.WriteLine (x == y); // False

Object.Equals method is resolved at runtime - according to the object's actual type. In this example below, it calls Int32’s Equals method, which applies value equality to the operands, returning true. With reference types, Equals performs referential equality comparison by default; with structs, Equals performs structural comparison by calling Equals on each of its fields.

object x = 5, y = 5;
Console.WriteLine (x.Equals (y)); // True

Occasionally, you need to force referential equality comparison. The static object.ReferenceEquals method

Since the Equals method has a parameter of type Object, a cast will be needed in order to be able to access class specific members. Tus A consequence of calling object.Equals is that it forces boxing on value types. This is undesirable in highly performance-sensitive scenarios because boxing is relatively expensive compared to the actual comparison.

The idea is that IEquatable<T>, when implemented, gives the same result as calling object’s virtual Equals method—but more quickly. Most basic .NET types implement IEquatable<T>. You can use IEquatable<T> as a constraint in a generic type:

class Test<T> where T : IEquatable<T>
public bool IsEqual (T a, T b)
return a.Equals (b); // No boxing with generic T

If we remove the generic constraint, the class would still compile, but a.Equals(b) would instead bind to the slower object.Equals (slower assuming T was a value type).

Note. the runtime will use the IEquatable.Equals method only if you pass an object with the same type as defined in the interface implementation; otherwise, it will use the Object.Equals method.
Therefore, in order to align the same result of both interface and object equals() method, we need to do both work:

Order Comparison

The basic protocols for determining the order of objects:

  • The IComparable interfaces (IComparable and IComparable<T>)
  • The > and < operators

The IComparable interfaces are defined as follows:

  • public interface IComparable { int CompareTo (object other); }
  • public interface IComparable<in T> { int CompareTo (T other); }

No comments:

Post a Comment