The content and code of this article is referenced from book Pro C#5.0 and the .NET 4.5 Framework by Apress. The intention of the writing is to review the konwledge and gain better understanding of the .net framework.
1. System Data Types and Corresponding C# keywords
The same as other programming languages, c# defines keywords for data types, used to represent local variables, class data member variables, method return values and parameters.
C# shorthand | System Type | Range |
bool | System.Boolean | true/false |
sbyte | System.SByte | -128 to 127 (8 bit number) |
byte | System.Byte | 0 to 255 (8 bit number) |
short | System.Int16 | -32768 to 32767 (16 bit number) |
ushort | System.UInt16 | 0 to 65535 (16 bit number) |
int | System.Int32 | 32 bit number |
uint | System.UInt32 | 32 bit number |
long | System.Int64 | 64 bit number |
ulong | System.UInt64 | 64 bit number |
char | System.Char | 16 bit character |
float | System.Single | 32 bit floating point number |
double | System.Double | 64 bit floating point number |
decimal | System.Decimal | 128 bit floating point number |
string |
1.1 Variable declaraction and Initialization
When you are declaring a local variable, you do so by specifying the data type followed by the variable's name. e.g int num1 = 0; string name="jason";
Be aware that it is a compiler error to make use a local variable before assigning an initial value.
1.2 Data type class hierarchy
In C#, all data type class are ultimately derived from System.Object. Also note that many numerical data types derived from a class named System.ValueType. All the descendents fo ValueType are automatically allocated on the stack, and , therefore, have a very predictable lifetime. On the other hand, other types such as Sytem.String, System.Array, System.Exception etc are not allocated on the stack, but on the garbage-collected heap.
1.2.1 Numerical Data Types
All numerical data types support MinValue/MaxValue properties. e.g int.MaxValue, double.MinValue
1.2.2 Boolean data type
The only valid values for System.Boolean type are "true | false", and bool.TrueString and bool.FalseString yields those value respectively.
1.2.3 System.Char
As you may already know, a string represents a contiguous set of characters (e.g "test"), while the char can represent a single slot in a string (e.g 't')
However, System.Char is able to provide you with more information about the value, whether it is digit, punctuation, or alphabetical.
char.IsDigit('h'), char.IsLetter('h'), char.IsWhiteSpace('hello ', 5), char.IsPunctuation('?')
1.2.4 Parsing value from string
The .net data types provide the ability to generate a variable of their underlying type given a textual equivalent.
e.g bool b = bool.Parse("True"); double b = double.Parse("99.884");
2. Working with string data
2.1 Basic string manipulation
1 public static void Main (string[] args) 2 { 3 string testString = "test from tim bo"; //string declaration 4 Console.WriteLine("value of the string is {0}", testString); 5 Console.WriteLine ("Length of the string is {0}", testString.Length); 6 Console.WriteLine ("string in uppercase: {0}", testString.ToUpper()); 7 Console.WriteLine ("string in lowercase: {0}", testString.ToLower()); 8 Console.WriteLine ("string contains the letter t? {0}", testString.Contains ("t")); 9 Console.WriteLine ("replace word test in the string with text, {0}", testString.Replace ("test", "text")); 10 }
2.2 String concatenation
string variables can be connected together to build larger strings via + operator, but underlying, the compiler is actually calling the static String.Concat() method. Both approachs are absolutely working, but + operator can save some keystrokes.
public static void Main (string[] args) { string text1 = "my name is "; string text2 = "tim"; Console.WriteLine (text1 + text2); Console.WriteLine(string.Concat(text1, text2)); }
2.3 Escape Characters
C# string literals may contains various escape characters, which qualify how the character data should be printed to the output stream. Each escape character begins with a backslash '\', followed by a specific token.
Character | Meaning |
\' | inserts a single quote into a string literal |
\" | insert a double quote into a string literal |
\\ | inserts a backslash into a string literal. |
\a | triggers a system alert |
\n | inserts a new line |
\r | inserts a carriage return |
\t | inserts a horizontal tab into the string literal |
public static void Main (string[] args) { Console.WriteLine("column1\tcolumn2\tcolumn3\t"); Console.WriteLine ("file path is C:\\documents\\chapter1"); Console.WriteLine ("insert new line. \n\n\n"); }
2.4 Define verbatim strings
when you prefix a string with the @ symbol, you have created what is termed a verbatim string. Using verbatim strings, you disable the procesing of a literal's escape characters and print out a string as it is.
Console.WriteLine(@"c:\documents\chapter1");
2.5 String equality
A reference type is an object allocated on the garbage-collected managed heap. By default, when you compare for equality on reference type (== or !=), you will be returned true if the references are pointing to the same object in memory. However, even though string data type is indeed a reference type, the equality operators have been redefined to compre the values of strings, not the object in memory to which they refer.
1 public static void Main (string[] args) 2 { 3 //variables 4 string s1 = "hello"; 5 string s2 = "test"; 6 7 //test equality 8 Console.WriteLine("s1 == s2, {0}", s1 == s2); //return false 9 Console.WriteLine("s1 == Hello, {0}", s1 == "Hello"); //return false, case sensitive 10 Console.WriteLine("s1.Equal(s2), {0}", s1.Equals(s2));//return false; 11 12 }
2.6 strings are immutable
One of the interesting aspects of System.String is that after you assign a string object with its initial value, the character data cannot be changed. The methods of the string type are, in fact, returning you a brand new string object in a modified format. So the string class can be inefficient and result in bloated code if misued, especially when performing string concatenation.
Fortunately, .net does provide a class named StringBuilder within System.Text to resolve this issue. When you call members of this type, you are directly modifying the object's internal character data.
1 public static void Main (string[] args) 2 { 3 //declare string builder 4 StringBuilder sb = new StringBuilder("Initial value"); 5 //append value to sb 6 sb.AppendLine ("line1"); 7 sb.AppendLine ("line2"); 8 Console.WriteLine (sb.ToString ()); 9 }
3. Narrowing and widening data type conversions
3.1Widening data
1 public static void Main (string[] args) 2 { 3 short number1 = 9, number2 = 10; 4 Console.WriteLine ("{0} + {1} = {2}", number1, number2, Add(number1, number2)); 5 } 6 7 static int Add(int x , int y) //method which accept two int number, and returns the sum of two ints 8 { 9 return x + y; 10 }
When we look at the example, notice that Add method excepts to be sent two int parameters. However, the main method is, in fact, sending two short variables. While this might seem like a complete and total mismatch of data types, the program compiles and executes without error and returns 19 as result.
The reason the compier runs successfully is due to the fact that there is no loss of data. The compiler implicitly widens each short to an int. Formally speaking, widening is the term used to define an implicit upward cast that does not result in a loss of data, e.g int to float, float to double.
3.2 Narrowing data
Formally speaking, the CLR (common language runtime) was unable to apply a narrowing operations. As you can guess, narrowing is the logic opposite of widening, in that a larger value is stored within a smaller data type variable. It is important to point out that all narrowing conversions result in a compiler error.
public static void Main (string[] args) { short number1 = 30000, number2 = 30000; short answer = Add (number1, number2); //error Console.WriteLine ("{0} + {1} = {2}", number1, number2, Add(number1, number2)); }
The compiler error occurs becuase the answer 60000 can not be stores as short, as it overflows the bounds of this data type.
When you wish to inform the compiler that you are willing to deal with a possible loss of data due to a narrowing operation, you must apply an explicit cast using the C# casting operator().
1 static void NarrowingAttempt() 2 { 3 byte myByte = 0; 4 int myInt = 200; 5 6 //explicityly cast the int into a byte 7 myByte = (byte)myInt; 8 Console.WriteLine ("Value of myByte:{0}", myByte); 9 }
3.3 Checked keywork
1 public static void Main (string[] args) 2 { 3 //byte valuel can go up to 255 4 byte b1 = 100; 5 byte b2 = 250; 6 7 byte sum = (byte)(b1 + b2); 8 Console.WriteLine ("The sum value is {0}", sum); //the result is 94 9 }
You might be surprised to get sum result 94, instead of 350. Actually, the reason is simple, given that System.Byte can hold a value between 0 to 255, sum not contains the overflow value 350 - 256 = 94.
To handle the overflow or underflow conditions in you application, you have two options. Your first choice is to leverage your wits and programming skills to handle it manually. The other option is to use keywork checked. If an overflow has occurred, you will receive a runtim exception: System.OverflowException.
1 public static void Main (string[] args) 2 { 3 //byte valuel can go up to 255 4 byte b1 = 100; 5 byte b2 = 250; 6 try 7 { 8 checked 9 { 10 byte sum = (byte)(b1 + b2); 11 Console.WriteLine ("The sum value is {0}", sum); 12 } 13 } 14 catch(OverflowException ex) 15 { 16 Console.WriteLine (ex.Message); 17 } 18 19 }
3.4 Setting project-wide overflow checking
As an alternative, the c# compiler supports the /checked flag. When enabled, all of your arithmetic will be evaluated for overflow without the need to make use of the c# checked keyword.
3.5 Unchecked
Now, assuming you have enabled this project-wide setting, given that the /checked flag will evaluate all arithmetric logic, c# provides the unchecked keyword to disable the throwing of an overflow exception.
1 unchecked 2 { 3 byte sum = (byte)(b1 + b2); 4 Console.WriteLine ("The sum value is {0}", sum); 5 }
4. Understanding implicity typed local variables
Up until this point in the chapter, when we have been defining local variables, we're explicitly specified the underlying data type of each variable being declared. c# does provide for implicitly typing of local variables using the var keyword. The var keyword can be used in place of specifying a specific data type. And the compiler will automatically infer the underlying data type based on the initial value used to initialize the local data point.
1 public static void Main (string[] args) 2 { 3 var myInt = 0; //int type 4 var myBool = true; //boolean type 5 var myString = "test"; //string 6 }
4.1 restriction on implicity type
There are various restrictions regarding to the use of the var keyword. First and foremost, implicit typing applies only to local variables in a method or property scope. It is illegal to use the var keyword to define return values, parameters, or field data of a custom type.
Also, local variables declared with var keyword must be assigned an initial value at the exact time of declaration and cannot be assigned the initial value of null. It make sense, because the compiler is relying on the value to decide the underlying data type.
var myData; //error
var myInt; //error
myInt = 0;
4.2 implicit typed data is strongly typed data
Be very aware that implicit typing of local variables results in storngly typed data. Therefore, use of the var keyword is not the same technique used with scripting language, where a variable can hold values of different types over its lifetime. (often termed dynamic typing)
1 public static void Main (string[] args) 2 { 3 var s = "string variable"; 4 s = "this is fine"; 5 6 string upperValeu = s.ToUpper (); 7 8 s = 44; //error 9 }
4.3 Usefulness of implicitly typed local variables
Implicit variable is extremely helpful with LINQ technology, as it makes use of query expression that can yield dynamically created result sets based on the format of the query itself.
1 public static void Main (string[] args) 2 { 3 int[] numbers = { 10, 20, 30, 40, 50 }; 4 //linq query 5 var subset = from i in numbers where i < 30 select i; 6 7 foreach (var i in subset) { 8 Console.WriteLine ("{0}", i); 9 } 10 }
5. C# iteration Construts
5.1 for loop
1 public static void Main (string[] args) 2 { 3 for (int i = 0; i < 4; i++) { 4 Console.WriteLine ("number is {0}", i); 5 } 6 }
5.2 foreach loop
The c# foreach keyword allows you to iterate over all items in a container without the need to test for an upper limit. In addition, unlike for loop, the foreach loop will only walk the contianer in a linear fashion. It is a perfect choice to walk a collection item by item.
1 public static void Main (string[] args) 2 { 3 string[] carTypes = { "Ford", "BMW", "Yugo", "Honda" }; 4 5 foreach (string car in carTypes) { 6 Console.WriteLine (car); 7 } 8 }
Any class that implements IEnumerable interfaces can be used in foreach loop.
5.3 while/ do while loop
The while looping construct is useful should you wish to execute a block of statements until some terminating condition has been reached.
1 public static void Main (string[] args) 2 { 3 string userIsDone = ""; 4 5 while (userIsDone.ToLower () != "yes") { 6 Console.WriteLine ("In while loop"); 7 Console.WriteLine ("Are you done? [yes][no]: "); 8 userIsDone = Console.ReadLine (); 9 } 10 }
1 public static void Main (string[] args) 2 { 3 string userIsDone = ""; 4 5 do { 6 Console.WriteLine ("In while loop"); 7 Console.WriteLine ("Are you done? [yes][no]: "); 8 userIsDone = Console.ReadLine (); 9 } while(userIsDone.ToLower () != "yes"); 10 }
The difference is that do/while loop are guaranteed to exeucte the corresponding block of code at least once. In contrast, it is possible that a simple while loop may never execute if the condition is false from the onset.
6. Decision constructs and the relational/equality operators
6.1 Equality and Relational operators
== | if(age == 30) | age equal 30 |
!= | if (age != 30) | age not equal 30 |
< | if (age < 30) | age is less than 30 |
> | if (age > 30) | age is greater than 30 |
<= | if (age <=30) | age is less than or equal 30 |
>= | if (age >= 30) | age is greater or equal 30 |
6.2 Conditional operators
&& | if (age == 30 && name == "tim") | And operator, return true is all expression are ture |
|| | if (age == 30 || name == "tim") | Or operator, return true is at least one express is ture |
! | if (!myBool) | Not operator, return ture if false, or false if true |
6.3 Switch statement
The Switch statement allows you to handle program flow based on a predefined set of choices.
1 public static void Main (string[] args) 2 { 3 string choice = Console.ReadLine (); 4 int n = int.Parse (choice); 5 6 switch (n) { 7 case 1: 8 Console.WriteLine ("Good choice"); 9 break; 10 case 2: 11 Console.WriteLine ("second choice"); 12 break; 13 default: 14 Console.WriteLine ("default choice"); 15 break; 16 } 17 }
one nice feature of the C# switch statement is that you can evaluate string data or enumeration type in addition to numeric data.