C# Notes for Professionals PDF
Document Details
Tags
Summary
This document is a comprehensive guide to C# programming, providing professional hints, tricks, and tutorials.
Full Transcript
C# C# Notes for Professionals Notes for Professionals 700+ pages of professional hints and tricks...
C# C# Notes for Professionals Notes for Professionals 700+ pages of professional hints and tricks Disclaimer GoalKicker.com This is an unocial free book created for educational purposes and is not aliated with ocial C# group(s) or company(s). Free Programming Books All trademarks and registered trademarks are the property of their respective owners Contents About................................................................................................................................................................................... 1 Chapter 1: Getting started with C# Language............................................................................................... 2 Section 1.1: Creating a new console application (Visual Studio)............................................................................... 2 Section 1.2: Creating a new project in Visual Studio (console application) and Running it in Debug mode.................................................................................................................................................................................. 4 Section 1.3: Creating a new program using.NET Core.............................................................................................. 7 Section 1.4: Creating a new program using Mono..................................................................................................... 9 Section 1.5: Creating a new query using LinqPad...................................................................................................... 9 Section 1.6: Creating a new project using Xamarin Studio...................................................................................... 12 Chapter 2: Literals...................................................................................................................................................... 18 Section 2.1: uint literals................................................................................................................................................ 18 Section 2.2: int literals................................................................................................................................................. 18 Section 2.3: sbyte literals............................................................................................................................................ 18 Section 2.4: decimal literals........................................................................................................................................ 18 Section 2.5: double literals.......................................................................................................................................... 18 Section 2.6: float literals.............................................................................................................................................. 18 Section 2.7: long literals.............................................................................................................................................. 18 Section 2.8: ulong literal............................................................................................................................................. 18 Section 2.9: string literals............................................................................................................................................ 19 Section 2.10: char literals............................................................................................................................................ 19 Section 2.11: byte literals............................................................................................................................................. 19 Section 2.12: short literal............................................................................................................................................. 19 Section 2.13: ushort literal........................................................................................................................................... 19 Section 2.14: bool literals............................................................................................................................................. 19 Chapter 3: Operators................................................................................................................................................ 20 Section 3.1: Overloadable Operators......................................................................................................................... 20 Section 3.2: Overloading equality operators............................................................................................................ 21 Section 3.3: Relational Operators.............................................................................................................................. 22 Section 3.4: Implicit Cast and Explicit Cast Operators............................................................................................. 24 Section 3.5: Short-circuiting Operators..................................................................................................................... 25 Section 3.6: ? : Ternary Operator............................................................................................................................... 26 Section 3.7: ?. (Null Conditional Operator)................................................................................................................ 27 Section 3.8: "Exclusive or" Operator.......................................................................................................................... 27 Section 3.9: default Operator..................................................................................................................................... 28 Section 3.10: Assignment operator '='........................................................................................................................ 28 Section 3.11: sizeof........................................................................................................................................................ 28 Section 3.12: ?? Null-Coalescing Operator................................................................................................................ 29 Section 3.13: Bit-Shifting Operators........................................................................................................................... 29 Section 3.14: => Lambda operator............................................................................................................................. 29 Section 3.15: Class Member Operators: Null Conditional Member Access............................................................ 31 Section 3.16: Class Member Operators: Null Conditional Indexing......................................................................... 31 Section 3.17: Postfix and Prefix increment and decrement..................................................................................... 31 Section 3.18: typeof..................................................................................................................................................... 32 Section 3.19: Binary operators with assignment...................................................................................................... 32 Section 3.20: nameof Operator................................................................................................................................. 32 Section 3.21: Class Member Operators: Member Access........................................................................................ 33 Section 3.22: Class Member Operators: Function Invocation................................................................................. 33 Section 3.23: Class Member Operators: Aggregate Object Indexing.................................................................... 33 Chapter 4: Conditional Statements.................................................................................................................. 34 Section 4.1: If-Else Statement..................................................................................................................................... 34 Section 4.2: If statement conditions are standard boolean expressions and values.......................................... 34 Section 4.3: If-Else If-Else Statement......................................................................................................................... 35 Chapter 5: Equality Operator............................................................................................................................... 36 Section 5.1: Equality kinds in c# and equality operator........................................................................................... 36 Chapter 6: Equals and GetHashCode............................................................................................................... 37 Section 6.1: Writing a good GetHashCode override................................................................................................. 37 Section 6.2: Default Equals behavior......................................................................................................................... 37 Section 6.3: Override Equals and GetHashCode on custom types........................................................................ 38 Section 6.4: Equals and GetHashCode in IEqualityComparator............................................................................. 39 Chapter 7: Null-Coalescing Operator............................................................................................................... 41 Section 7.1: Basic usage.............................................................................................................................................. 41 Section 7.2: Null fall-through and chaining.............................................................................................................. 41 Section 7.3: Null coalescing operator with method calls......................................................................................... 42 Section 7.4: Use existing or create new.................................................................................................................... 43 Section 7.5: Lazy properties initialization with null coalescing operator............................................................... 43 Chapter 8: Null-conditional Operators............................................................................................................ 44 Section 8.1: Null-Conditional Operator...................................................................................................................... 44 Section 8.2: The Null-Conditional Index.................................................................................................................... 44 Section 8.3: Avoiding NullReferenceExceptions....................................................................................................... 45 Section 8.4: Null-conditional Operator can be used with Extension Method........................................................ 45 Chapter 9: nameof Operator................................................................................................................................ 47 Section 9.1: Basic usage: Printing a variable name.................................................................................................. 47 Section 9.2: Raising PropertyChanged event........................................................................................................... 47 Section 9.3: Argument Checking and Guard Clauses.............................................................................................. 48 Section 9.4: Strongly typed MVC action links........................................................................................................... 48 Section 9.5: Handling PropertyChanged events...................................................................................................... 49 Section 9.6: Applied to a generic type parameter................................................................................................... 49 Section 9.7: Printing a parameter name................................................................................................................... 50 Section 9.8: Applied to qualified identifiers............................................................................................................... 50 Chapter 10: Verbatim Strings............................................................................................................................... 51 Section 10.1: Interpolated Verbatim Strings.............................................................................................................. 51 Section 10.2: Escaping Double Quotes...................................................................................................................... 51 Section 10.3: Verbatim strings instruct the compiler to not use character escapes............................................ 51 Section 10.4: Multiline Strings..................................................................................................................................... 52 Chapter 11: Common String Operations.......................................................................................................... 53 Section 11.1: Formatting a string................................................................................................................................. 53 Section 11.2: Correctly reversing a string.................................................................................................................. 53 Section 11.3: Padding a string to a fixed length........................................................................................................ 54 Section 11.4: Getting x characters from the right side of a string........................................................................... 55 Section 11.5: Checking for empty String using String.IsNullOrEmpty() and String.IsNullOrWhiteSpace()................................................................................................................................................................................ 56 Section 11.6: Trimming Unwanted Characters O the Start and/or End of Strings............................................. 57 Section 11.7: Convert Decimal Number to Binary,Octal and Hexadecimal Format.............................................. 57 Section 11.8: Construct a string from Array.............................................................................................................. 57 Section 11.9: Formatting using ToString.................................................................................................................... 58 Section 11.10: Splitting a String by another string..................................................................................................... 59 Section 11.11: Splitting a String by specific character................................................................................................ 59 Section 11.12: Getting Substrings of a given string................................................................................................... 59 Section 11.13: Determine whether a string begins with a given sequence............................................................. 59 Section 11.14: Getting a char at specific index and enumerating the string........................................................... 59 Section 11.15: Joining an array of strings into a new one........................................................................................ 60 Section 11.16: Replacing a string within a string........................................................................................................ 60 Section 11.17: Changing the case of characters within a String.............................................................................. 60 Section 11.18: Concatenate an array of strings into a single string........................................................................ 61 Section 11.19: String Concatenation............................................................................................................................ 61 Chapter 12: String.Format...................................................................................................................................... 62 Section 12.1: Since C# 6.0............................................................................................................................................ 62 Section 12.2: Places where String.Format is 'embedded' in the framework.......................................................... 62 Section 12.3: Create a custom format provider....................................................................................................... 62 Section 12.4: Date Formatting.................................................................................................................................... 63 Section 12.5: Currency Formatting............................................................................................................................ 64 Section 12.6: Using custom number format.............................................................................................................. 65 Section 12.7: Align left/ right, pad with spaces......................................................................................................... 65 Section 12.8: Numeric formats................................................................................................................................... 66 Section 12.9: ToString()............................................................................................................................................... 66 Section 12.10: Escaping curly brackets inside a String.Format() expression......................................................... 67 Section 12.11: Relationship with ToString()................................................................................................................ 67 Chapter 13: String Concatenate.......................................................................................................................... 68 Section 13.1: + Operator............................................................................................................................................... 68 Section 13.2: Concatenate strings using System.Text.StringBuilder....................................................................... 68 Section 13.3: Concat string array elements using String.Join................................................................................. 68 Section 13.4: Concatenation of two strings using $................................................................................................. 69 Chapter 14: String Manipulation......................................................................................................................... 70 Section 14.1: Replacing a string within a string......................................................................................................... 70 Section 14.2: Finding a string within a string............................................................................................................. 70 Section 14.3: Removing (Trimming) white-space from a string............................................................................. 70 Section 14.4: Splitting a string using a delimiter....................................................................................................... 71 Section 14.5: Concatenate an array of strings into a single string......................................................................... 71 Section 14.6: String Concatenation............................................................................................................................ 71 Section 14.7: Changing the case of characters within a String............................................................................... 71 Chapter 15: String Interpolation......................................................................................................................... 73 Section 15.1: Format dates in strings......................................................................................................................... 73 Section 15.2: Padding the output............................................................................................................................... 73 Section 15.3: Expressions............................................................................................................................................. 74 Section 15.4: Formatting numbers in strings............................................................................................................ 74 Section 15.5: Simple Usage......................................................................................................................................... 75 Chapter 16: String Escape Sequences.............................................................................................................. 76 Section 16.1: Escaping special symbols in string literals.......................................................................................... 76 Section 16.2: Unicode character escape sequences................................................................................................ 76 Section 16.3: Escaping special symbols in character literals.................................................................................. 76 Section 16.4: Using escape sequences in identifiers................................................................................................ 76 Section 16.5: Unrecognized escape sequences produce compile-time errors..................................................... 77 Chapter 17: StringBuilder........................................................................................................................................ 78 Section 17.1: What a StringBuilder is and when to use one..................................................................................... 78 Section 17.2: Use StringBuilder to create string from a large number of records............................................... 79 Chapter 18: Regex Parsing..................................................................................................................................... 80 Section 18.1: Single match........................................................................................................................................... 80 Section 18.2: Multiple matches................................................................................................................................... 80 Chapter 19: DateTime Methods........................................................................................................................... 81 Section 19.1: DateTime Formatting............................................................................................................................ 81 Section 19.2: DateTime.AddDays(Double)................................................................................................................ 82 Section 19.3: DateTime.AddHours(Double)............................................................................................................... 82 Section 19.4: DateTime.Parse(String)........................................................................................................................ 82 Section 19.5: DateTime.TryParse(String, DateTime)................................................................................................ 82 Section 19.6: DateTime.AddMilliseconds(Double).................................................................................................... 83 Section 19.7: DateTime.Compare(DateTime t1, DateTime t2 )................................................................................ 83 Section 19.8: DateTime.DaysInMonth(Int32, Int32)................................................................................................. 83 Section 19.9: DateTime.AddYears(Int32)................................................................................................................... 84 Section 19.10: Pure functions warning when dealing with DateTime..................................................................... 84 Section 19.11: DateTime.TryParseExact(String, String, IFormatProvider, DateTimeStyles, DateTime)................................................................................................................................................................................ 84 Section 19.12: DateTime.Add(TimeSpan)................................................................................................................... 86 Section 19.13: Parse and TryParse with culture info................................................................................................. 86 Section 19.14: DateTime as initializer in for-loop...................................................................................................... 87 Section 19.15: DateTime.ParseExact(String, String, IFormatProvider)................................................................. 87 Section 19.16: DateTime ToString, ToShortDateString, ToLongDateString and ToString formatted................. 88 Section 19.17: Current Date......................................................................................................................................... 88 Chapter 20: Arrays..................................................................................................................................................... 89 Section 20.1: Declaring an array................................................................................................................................ 89 Section 20.2: Initializing an array filled with a repeated non-default value......................................................... 89 Section 20.3: Copying arrays..................................................................................................................................... 90 Section 20.4: Comparing arrays for equality........................................................................................................... 90 Section 20.5: Multi-dimensional arrays..................................................................................................................... 91 Section 20.6: Getting and setting array values........................................................................................................ 91 Section 20.7: Iterate over an array............................................................................................................................ 91 Section 20.8: Creating an array of sequential numbers......................................................................................... 92 Section 20.9: Jagged arrays...................................................................................................................................... 92 Section 20.10: Array covariance................................................................................................................................ 94 Section 20.11: Arrays as IEnumerable instances................................................................................................... 94 Section 20.12: Checking if one array contains another array................................................................................ 94 Chapter 21: O(n) Algorithm for circular rotation of an array.............................................................. 96 Section 21.1: Example of a generic method that rotates an array by a given shift............................................. 96 Chapter 22: Enum....................................................................................................................................................... 98 Section 22.1: Enum basics........................................................................................................................................... 98 Section 22.2: Enum as flags....................................................................................................................................... 99 Section 22.3: Using also needs to be overloaded). A full list of overloadable operators (as well as non-overloadable operators and the restrictions placed on some overloadable operators) can be seen at MSDN - Overloadable Operators (C# Programming Guide). GoalKicker.com – C# Notes for Professionals 20 Version ≥ 7.0 overloading of operator is was introduced with the pattern matching mechanism of C# 7.0. For details see Pattern Matching Given a type Cartesian defined as follows public class Cartesian { public int X { get; } public int Y { get; } } An overloadable operator is could e.g. be defined for Polar coordinates public static class Polar { public static bool operator is(Cartesian c, out double R, out double Theta) { R = Math.Sqrt(c.X*c.X + c.Y*c.Y); Theta = Math.Atan2(c.Y, c.X); return c.X != 0 || c.Y != 0; } } which can be used like this var c = Cartesian(3, 4); if (c is Polar(var R, *)) { Console.WriteLine(R); } (The example is taken from the Roslyn Pattern Matching Documentation) Section 3.2: Overloading equality operators Overloading just equality operators is not enough. Under different circumstances, all of the following can be called: 1. object.Equals and object.GetHashCode 2. IEquatable.Equals (optional, allows avoiding boxing) 3. operator == and operator != (optional, allows using operators) When overriding Equals, GetHashCode must also be overridden. When implementing Equals, there are many special cases: comparing to objects of a different type, comparing to self etc. When NOT overridden Equals method and == operator behave differently for classes and structs. For classes just references are compared, and for structs values of properties are compared via reflection what can negatively affect performance. == can not be used for comparing structs unless it is overridden. Generally equality operation must obey the following rules: Must not throw exceptions. Reflexivity: A always equals A (may not be true for NULL values in some systems). Transitvity: if A equals B, and B equals C, then A equals C. If A equals B, then A and B have equal hash codes. GoalKicker.com – C# Notes for Professionals 21 Inheritance tree independence: if B and C are instances of Class2 inherited from Class1: Class1.Equals(A,B) must always return the same value as the call to Class2.Equals(A,B). class Student : IEquatable { public string Name { get; set; } = ""; public bool Equals(Student other) { if (ReferenceEquals(other, null)) return false; if (ReferenceEquals(other, this)) return true; return string.Equals(Name, other.Name); } public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; return Equals(obj as Student); } public override int GetHashCode() { return Name?.GetHashCode() ?? 0; } public static bool operator ==(Student left, Student right) { return Equals(left, right); } public static bool operator !=(Student left, Student right) { return !Equals(left, right); } } Section 3.3: Relational Operators Equals Checks whether the supplied operands (arguments) are equal "a" == "b" // Returns false. "a" == "a" // Returns true. 1 == 0 // Returns false. 1 == 1 // Returns true. false == true // Returns false. false == false // Returns true. Unlike Java, the equality comparison operator works natively with strings. The equality comparison operator will work with operands of differing types if an implicit cast exists from one to the other. If no suitable implicit cast exists, you may call an explicit cast or use a method to convert to a compatible type. 1 == 1.0 // Returns true because there is an implicit cast from int to double. new Object() == 1.0 // Will not compile. GoalKicker.com – C# Notes for Professionals 22 MyStruct.AsInt() == 1 // Calls AsInt() on MyStruct and compares the resulting int with 1. Unlike Visual Basic.NET, the equality comparison operator is not the same as the equality assignment operator. var x = new Object(); var y = new Object(); x == y // Returns false, the operands (objects in this case) have different references. x == x // Returns true, both operands have the same reference. Not to be confused with the assignment operator (=). For value types, the operator returns true if both operands are equal in value. For reference types, the operator returns true if both operands are equal in reference (not value). An exception is that string objects will be compared with value equality. Not Equals Checks whether the supplied operands are not equal. "a" != "b" // Returns true. "a" != "a" // Returns false. 1 != 0 // Returns true. 1 != 1 // Returns false. false != true // Returns true. false != false // Returns false. var x = new Object(); var y = new Object(); x != y // Returns true, the operands have different references. x != x // Returns false, both operands have the same reference. This operator effectively returns the opposite result to that of the equals (==) operator Greater Than Checks whether the first operand is greater than the second operand. 3 > 5 //Returns false. 1 > 0 //Returns true. 2 > 2 //Return false. var x = 10; var y = 15; x > y //Returns false. y > x //Returns true. Less Than Checks whether the first operand is less than the second operand. 2 < 4 //Returns true. 1 < -3 //Returns false. 2 < 2 //Return false. var x = 12; var y = 22; x < y //Returns true. y < x //Returns false. GoalKicker.com – C# Notes for Professionals 23 Greater Than Equal To Checks whether the first operand is greater than equal to the second operand. 7 >= 8 //Returns false. 0 >= 0 //Returns true. Less Than Equal To Checks whether the first operand is less than equal to the second operand. 2 x.Id == 2) ?? new MyClass { Id = 2 }; Section 7.5: Lazy properties initialization with null coalescing operator private List _fooBars; public List FooBars { get { return _fooBars ?? (_fooBars = new List()); } } The first time the property.FooBars is accessed the _fooBars variable will evaluate as null, thus falling through to the assignment statement assigns and evaluates to the resulting value. Thread safety This is not thread-safe way of implementing lazy properties. For thread-safe laziness, use the Lazy class built into the.NET Framework. C# 6 Syntactic Sugar using expression bodies Note that since C# 6, this syntax can be simplified using expression body for the property: private List _fooBars; public List FooBars => _fooBars ?? ( _fooBars = new List() ); Subsequent accesses to the property will yield the value stored in the _fooBars variable. Example in the MVVM pattern This is often used when implementing commands in the MVVM pattern. Instead of initializing the commands eagerly with the construction of a viewmodel, commands are lazily initialized using this pattern as follows: private ICommand _actionCommand = null; public ICommand ActionCommand => _actionCommand ?? ( _actionCommand = new DelegateCommand( DoAction ) ); GoalKicker.com – C# Notes for Professionals 43 Chapter 8: Null-conditional Operators Section 8.1: Null-Conditional Operator The ?. operator is syntactic sugar to avoid verbose null checks. It's also known as the Safe navigation operator. Class used in the following example: public class Person { public int Age { get; set; } public string Name { get; set; } public Person Spouse { get; set; } } If an object is potentially null (such as a function that returns a reference type) the object must first be checked for null to prevent a possible NullReferenceException. Without the null-conditional operator, this would look like: Person person = GetPerson(); int? age = null; if (person != null) age = person.Age; The same example using the null-conditional operator: Person person = GetPerson(); var age = person?.Age; // 'age' will be of type 'int?', even if 'person' is not null Chaining the Operator The null-conditional operator can be combined on the members and sub-members of an object. // Will be null if either `person` or `person.Spouse` are null int? spouseAge = person?.Spouse?.Age; Combining with the Null-Coalescing Operator The null-conditional operator can be combined with the null-coalescing operator to provide a default value: // spouseDisplayName will be "N/A" if person, Spouse, or Name is null var spouseDisplayName = person?.Spouse?.Name ?? "N/A"; Section 8.2: The Null-Conditional Index Similarly to the ?. operator, the null-conditional index operator checks for null values when indexing into a collection that may be null. string item = collection?[index]; is syntactic sugar for string item = null; GoalKicker.com – C# Notes for Professionals 44 if(collection != null) { item = collection[index]; } Section 8.3: Avoiding NullReferenceExceptions var person = new Person { Address = null; }; var city = person.Address.City; //throws a NullReferenceException var nullableCity = person.Address?.City; //returns the value of null This effect can be chained together: var person = new Person { Address = new Address { State = new State { Country = null } } }; // this will always return a value of at least "null" to be stored instead // of throwing a NullReferenceException var countryName = person?.Address?.State?.Country?.Name; Section 8.4: Null-conditional Operator can be used with Extension Method Extension Method can work on null references, but you can use ?. to null-check anyway. public class Person { public string Name {get; set;} } public static class PersonExtensions { public static int GetNameLength(this Person person) { return person == null ? -1 : person.Name.Length; } } Normally, the method will be triggered for null references, and return -1: Person person = null; int nameLength = person.GetNameLength(); // returns -1 Using ?. the method will not be triggered for null references, and the type is int?: GoalKicker.com – C# Notes for Professionals 45 Person person = null; int? nameLength = person?.GetNameLength(); // nameLength is null. This behavior is actually expected from the way in which the ?. operator works: it will avoid making instance method calls for null instances, in order to avoid NullReferenceExceptions. However, the same logic applies to the extension method, despite the difference on how the method is declared. For more information on why the extension method is called in the first example, please see the Extension methods - null checking documentation. GoalKicker.com – C# Notes for Professionals 46 Chapter 9: nameof Operator The nameof operator allows you to get the name of a variable, type or member in string form without hard-coding it as a literal. The operation is evaluated at compile-time, which means that you can rename a referenced identifier, using an IDE's rename feature, and the name string will update with it. Section 9.1: Basic usage: Printing a variable name The nameof operator allows you to get the name of a variable, type or member in string form without hard-coding it as a literal. The operation is evaluated at compile-time, which means that you can rename, using an IDE's rename feature, a referenced identifier and the name string will update with it. var myString = "String Contents"; Console.WriteLine(nameof(myString)); Would output myString because the name of the variable is "myString". Refactoring the variable name would change the string. If called on a reference type, the nameof operator returns the name of the current reference, not the name or type name of the underlying object. For example: string greeting = "Hello!"; Object mailMessageBody = greeting; Console.WriteLine(nameof(greeting)); // Returns "greeting" Console.WriteLine(nameof(mailMessageBody)); // Returns "mailMessageBody", NOT "greeting"! Section 9.2: Raising PropertyChanged event Snippet public class Person : INotifyPropertyChanged { private string _address; public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public string Address { get { return _address; } set { if (_address == value) { GoalKicker.com – C# Notes for Professionals 47 return; } _address = value; OnPropertyChanged(nameof(Address)); } } }... var person = new Person(); person.PropertyChanged += (s,e) => Console.WriteLine(e.PropertyName); person.Address = "123 Fake Street"; Console Output Address Section 9.3: Argument Checking and Guard Clauses Prefer public class Order { public OrderLine AddOrderLine(OrderLine orderLine) { if (orderLine == null) throw new ArgumentNullException(nameof(orderLine));... } } Over public class Order { public OrderLine AddOrderLine(OrderLine orderLine) { if (orderLine == null) throw new ArgumentNullException("orderLine");... } } Using the nameof feature makes it easier to refactor method parameters. Section 9.4: Strongly typed MVC action links Instead of the usual loosely typed: @Html.ActionLink("Log in", "UserController", "LogIn") You can now make action links strongly typed: @Html.ActionLink("Log in", @typeof(UserController), @nameof(UserController.LogIn)) GoalKicker.com – C# Notes for Professionals 48 Now if you want to refactor your code and rename the UserController.LogIn method to UserController.SignIn, you don't need to worry about searching for all string occurrences. The compiler will do the job. Section 9.5: Handling PropertyChanged events Snippet public class BugReport : INotifyPropertyChanged { public string Title {... } public BugStatus Status {... } }... private void BugReport_PropertyChanged(object sender, PropertyChangedEventArgs e) { var bugReport = (BugReport)sender; switch (e.PropertyName) { case nameof(bugReport.Title): Console.WriteLine("{0} changed to {1}", e.PropertyName, bugReport.Title); break; case nameof(bugReport.Status): Console.WriteLine("{0} changed to {1}", e.PropertyName, bugReport.Status); break; } }... var report = new BugReport(); report.PropertyChanged += BugReport_PropertyChanged; report.Title = "Everything is on fire and broken"; report.Status = BugStatus.ShowStopper; Console Output Title changed to Everything is on fire and broken Status changed to ShowStopper Section 9.6: Applied to a generic type parameter Snippet public class SomeClass { public void PrintTypeName() { Console.WriteLine(nameof(TItem)); } } GoalKicker.com – C# Notes for Professionals 49... var myClass = new SomeClass(); myClass.PrintTypeName(); Console.WriteLine(nameof(SomeClass)); Console Output TItem SomeClass Section 9.7: Printing a parameter name Snippet public void DoSomething(int paramValue) { Console.WriteLine(nameof(paramValue)); }... int myValue = 10; DoSomething(myValue); Console Output paramValue Section 9.8: Applied to qualified identifiers Snippet Console.WriteLine(nameof(CompanyNamespace.MyNamespace)); Console.WriteLine(nameof(MyClass)); Console.WriteLine(nameof(MyClass.MyNestedClass)); Console.WriteLine(nameof(MyNamespace.MyClass.MyNestedClass.MyStaticProperty)); Console Output MyNamespace MyClass MyNestedClass MyStaticProperty GoalKicker.com – C# Notes for Professionals 50 Chapter 10: Verbatim Strings Section 10.1: Interpolated Verbatim Strings Verbatim strings can be combined with the new String interpolation features found in C#6. Console.WriteLine($@"Testing \n 1 2 {5 - 2} New line"); Output: Testing \n 1 2 3 New line Live Demo on.NET Fiddle As expected from a verbatim string, the backslashes are ignored as escape characters. And as expected from an interpolated string, any expression inside curly braces is evaluated before being inserted into the string at that position. Section 10.2: Escaping Double Quotes Double Quotes inside verbatim strings can be escaped by using 2 sequential double quotes "" to represent one double quote " in the resulting string. var str = @"""I don't think so,"" he said."; Console.WriteLine(str); Output: "I don't think so," he said. Live Demo on.NET Fiddle Section 10.3: Verbatim strings instruct the compiler to not use character escapes In a normal string, the backslash character is the escape character, which instructs the compiler to look at the next character(s) to determine the actual character in the string. (Full list of character escapes) In verbatim strings, there are no character escapes (except for "" which is turned into a "). To use a verbatim string, just prepend a @ before the starting quotes. This verbatim string var filename = @"c:\temp\newfile.txt" Output: GoalKicker.com – C# Notes for Professionals 51 c:\temp\newfile.txt As opposed to using an ordinary (non-verbatim) string: var filename = "c:\temp\newfile.txt" that will output: c: emp ewfile.txt using character escaping. (The \t is replaced with a tab character and the \n is replace with a newline.) Live Demo on.NET Fiddle Section 10.4: Multiline Strings var multiLine = @"This is a multiline paragraph"; Output: This is a multiline paragraph Live Demo on.NET Fiddle Multi-line strings that contain double-quotes can also be escaped just as they were on a single line, because they are verbatim strings. var multilineWithDoubleQuotes = @"I went to a city named ""San Diego"" during summer vacation."; Live Demo on.NET Fiddle It should be noted that the spaces/tabulations at the start of lines 2 and 3 here are actually present in the value of the variable; check this question for possible solutions. GoalKicker.com – C# Notes for Professionals 52 Chapter 11: Common String Operations Section 11.1: Formatting a string Use the String.Format() method to replace one or more items in the string with the string representation of a specified object: String.Format("Hello {0} Foo {1}", "World", "Bar") //Hello World Foo Bar Section 11.2: Correctly reversing a string Most times when people have to reverse a string, they do it more or less like this: char[] a = s.ToCharArray(); System.Array.Reverse(a); string r = new string(a); However, what these people don't realize is that this is actually wrong. And I don't mean because of the missing NULL check. It is actually wrong because a Glyph/GraphemeCluster can consist out of several codepoints (aka. characters). To see why this is so, we first have to be aware of the fact what the term "character" actually means. Reference: Character is an overloaded term than can mean many things. A code point is the atomic unit of information. Text is a sequence of code points. Each code point is a number which is given meaning by the Unicode standard. A grapheme is a sequence of one or more code points that are displayed as a single, graphical unit that a reader recognizes as a single element of the writing system. For example, both a and ä are graphemes, but they may consist of multiple code points (e.g. ä may be two code points, one for the base character a followed by one for the diaresis; but there's also an alternative, legacy, single code point representing this grapheme). Some code points are never part of any grapheme (e.g. the zero-width non-joiner, or directional overrides). A glyph is an image, usually stored in a font (which is a collection of glyphs), used to represent graphemes or parts thereof. Fonts may compose multiple glyphs into a single representation, for example, if the above ä is a single code point, a font may chose to render that as two separate, spatially overlaid glyphs. For OTF, the font's GSUB and GPOS tables contain substitution and positioning information to make this work. A font may contain multiple alternative glyphs for the same grapheme, too. So in C#, a character is actually a CodePoint. Which means, if you just reverse a valid string like Les Misérables, which can look like this string s = "Les Mise\u0301rables"; as a sequence of characters, you will get: GoalKicker.com – C# Notes for Professionals 53 selbaŕesiM seL As you can see, the accent is on the R character, instead of the e character. Although string.reverse.reverse will yield the original string if you both times reverse the char array, this kind of reversal is definitely NOT the reverse of the original string. You'll need to reverse each GraphemeCluster only. So, if done correctly, you reverse a string like this: private static System.Collections.Generic.List GraphemeClusters(string s) { System.Collections.Generic.List ls = new System.Collections.Generic.List(); System.Globalization.TextElementEnumerator enumerator = System.Globalization.StringInfo.GetTextElementEnumerator(s); while (enumerator.MoveNext()) { ls.Add((string)enumerator.Current); } return ls; } // this private static string ReverseGraphemeClusters(string s) { if(string.IsNullOrEmpty(s) || s.Length == 1) return s; System.Collections.Generic.List ls = GraphemeClusters(s); ls.Reverse(); return string.Join("", ls.ToArray()); } public static void TestMe() { string s = "Les Mise\u0301rables"; // s = "noël"; string r = ReverseGraphemeClusters(s); // This would be wrong: // char[] a = s.ToCharArray(); // System.Array.Reverse(a); // string r = new string(a); System.Console.WriteLine(r); } And - oh joy - you'll realize if you do it correctly like this, it will also work for Asian/South-Asian/East-Asian languages (and French/Swedish/Norwegian, etc.)... Section 11.3: Padding a string to a fixed length string s = "Foo"; string paddedLeft = s.PadLeft(5); // paddedLeft = " Foo" (pads with spaces by default) string paddedRight = s.PadRight(6, '+'); // paddedRight = "Foo+++" GoalKicker.com – C# Notes for Professionals 54 string noPadded = s.PadLeft(2); // noPadded = "Foo" (original string is never shortened) Section 11.4: Getting x characters from the right side of a string Visual Basic has Left, Right, and Mid functions that returns characters from the Left, Right, and Middle of a string. These methods does not exist in C#, but can be implemented with Substring(). They can be implemented as an extension methods like the following: public static class StringExtensions { /// /// VB Left function /// /// /// /// Left-most numchars characters public static string Left( this string stringparam, int numchars ) { // Handle possible Null or numeric stringparam being passed stringparam += string.Empty; // Handle possible negative numchars being passed numchars = Math.Abs( numchars ); // Validate numchars parameter if (numchars > stringparam.Length) numchars = stringparam.Length; return stringparam.Substring( 0, numchars ); } /// /// VB Right function /// /// /// /// Right-most numchars characters public static string Right( this string stringparam, int numchars ) { // Handle possible Null or numeric stringparam being passed stringparam += string.Empty; // Handle possible negative numchars being passed numchars = Math.Abs( numchars ); // Validate numchars parameter if (numchars > stringparam.Length) numchars = stringparam.Length; return stringparam.Substring( stringparam.Length - numchars ); } /// /// VB Mid function - to end of string /// /// /// VB-Style startindex, 1st char startindex = 1 /// Balance of string beginning at startindex character public static string Mid( this string stringparam, int startindex ) GoalKicker.com – C# Notes for Professionals 55 { // Handle possible Null or numeric stringparam being passed stringparam += string.Empty; // Handle possible negative startindex being passed startindex = Math.Abs( startindex ); // Validate numchars parameter if (startindex > stringparam.Length) startindex = stringparam.Length; // C# strings are zero-based, convert passed startindex return stringparam.Substring( startindex - 1 ); } /// /// VB Mid function - for number of characters /// /// /// VB-Style startindex, 1st char startindex = 1 /// number of characters to return /// Balance of string beginning at startindex character public static string Mid( this string stringparam, int startindex, int numchars) { // Handle possible Null or numeric stringparam being passed stringparam += string.Empty; // Handle possible negative startindex being passed startindex = Math.Abs( startindex ); // Handle possible negative numchars being passed numchars = Math.Abs( numchars ); // Validate numchars parameter if (startindex > stringparam.Length) startindex = stringparam.Length; // C# strings are zero-based, convert passed startindex return stringparam.Substring( startindex - 1, numchars ); } } This extension method can be used as follows: string myLongString = "Hello World!"; string myShortString = myLongString.Right(6); // "World!" string myLeftString = myLongString.Left(5); // "Hello" string myMidString1 = myLongString.Left(4); // "lo World" string myMidString2 = myLongString.Left(2,3); // "ell" Section 11.5: Checking for empty String using String.IsNullOrEmpty() and String.IsNullOrWhiteSpace() string nullString = null; string emptyString = ""; string whitespaceString = " "; string tabString = "\t"; string newlineString = "\n"; string nonEmptyString = "abc123"; GoalKicker.com – C# Notes for Professionals 56 bool result; result = String.IsNullOrEmpty(nullString); // true result = String.IsNullOrEmpty(emptyString); // true result = String.IsNullOrEmpty(whitespaceString); // false result = String.IsNullOrEmpty(tabString); // false result = String.IsNullOrEmpty(newlineString); // false result = String.IsNullOrEmpty(nonEmptyString); // false result = String.IsNullOrWhiteSpace(nullString); // true result = String.IsNullOrWhiteSpace(emptyString); // true result = String.IsNullOrWhiteSpace(tabString); // true result = String.IsNullOrWhiteSpace(newlineString); // true result = String.IsNullOrWhiteSpace(whitespaceString); // true result = String.IsNullOrWhiteSpace(nonEmptyString); // false Section 11.6: Trimming Unwanted Characters O the Start and/or End of Strings String.Trim() string x = " Hello World! "; string y = x.Trim(); // "Hello World!" string q = "{(Hi!*"; string r = q.Trim( '(', '*', '{' ); // "Hi!" String.TrimStart() and String.TrimEnd() string q = "{(Hi*"; string r = q.TrimStart( '{' ); // "(Hi*" string s = q.TrimEnd( '*' ); // "{(Hi" Section 11.7: Convert Decimal Number to Binary,Octal and Hexadecimal Format 1. To convert decimal number to binary format use base 2 Int32 Number = 15; Console.WriteLine(Convert.ToString(Number, 2)); //OUTPUT : 1111 2. To convert decimal number to octal format use base 8 int Number = 15; Console.WriteLine(Convert.ToString(Number, 8)); //OUTPUT : 17 3. To convert decimal number to hexadecimal format use base 16 var Number = 15; Console.WriteLine(Convert.ToString(Number, 16)); //OUTPUT : f Section 11.8: Construct a string from Array The String.Join method will help us to construct a string From array/list of characters or string. This method accepts two parameters. The first one is the delimiter or the separator which will help you to separate each element in the array. And the second parameter is the Array itself. GoalKicker.com – C# Notes for Professionals 57 String from char array: string delimiter=","; char[] charArray = new[] { 'a', 'b', 'c' }; string inputString = String.Join(delimiter, charArray); Output : a,b,c if we change the delimiter as "" then the output will become abc. String from List of char: string delimiter = "|"; List charList = new List() { 'a', 'b', 'c' }; string inputString = String.Join(delimiter, charList); Output : a|b|c String from List of Strings: string delimiter = " "; List stringList = new List() { "Ram", "is", "a","boy" }; string inputString = String.Join(delimiter, stringList); Output : Ram is a boy String from array of strings: string delimiter = "_"; string[] stringArray = new [] { "Ram", "is", "a","boy" }; string inputString = String.Join(delimiter, stringArray); Output : Ram_is_a_boy Section 11.9: Formatting using ToString Usually we are using String.Format method for formatting purpose, the.ToString is usually used for converting other types to string. We can specify the format along with the ToString method while conversion is taking place, So we can avoid an additional Formatting. Let Me Explain how it works with different types; Integer to formatted string: int intValue = 10; string zeroPaddedInteger = intValue.ToString("000"); // Output will be "010" string customFormat = intValue.ToString("Input value is 0"); // output will be "Input value is 10" double to formatted string: double doubleValue = 10.456; string roundedDouble = doubleValue.ToString("0.00"); // output 10.46 string integerPart = doubleValue.ToString("00"); // output 10 string customFormat = doubleValue.ToString("Input value is 0.0"); // Input value is 10.5 Formatting DateTime using ToString DateTime currentDate = DateTime.Now; // {7/21/2016 7:23:15 PM} string dateTimeString = currentDate.ToString("dd-MM-yyyy HH:mm:ss"); // "21-07-2016 19:23:15" GoalKicker.com – C# Notes for Professionals 58 string dateOnlyString = currentDate.ToString("dd-MM-yyyy"); // "21-07-2016" string dateWithMonthInWords = currentDate.ToString("dd-MMMM-yyyy HH:mm:ss"); // "21-July-2016 19:23:15" Section 11.10: Splitting a String by another string string str = "this--is--a--complete--sentence"; string[] tokens = str.Split(new[] { "--" }, StringSplitOptions.None); Result: [ "this", "is", "a", "complete", "sentence" ] Section 11.11: Splitting a String by specific character string helloWorld = "hello world, how is it going?"; string[] parts1 = helloWorld.Split(','); //parts1: ["hello world", " how is it going?"] string[] parts2 = helloWorld.Split(' '); //parts2: ["hello", "world,", "how", "is", "it", "going?"] Section 11.12: Getting Substrings of a given string string helloWorld = "Hello World!"; string world = helloWorld.Substring(6); //world = "World!" string hello = helloWorld.Substring(0,5); // hello = "Hello" Substring returns the string up from a given index, or between two indexes (both inclusive). Section 11.13: Determine whether a string begins with a given sequence string HelloWorld = "Hello World"; HelloWorld.StartsWith("Hello"); // true HelloWorld.StartsWith("Foo"); // false Finding a string within a string Using the System.String.Contains you can find out if a particular string exists within a string. The method returns a boolean, true if the string exists else false. string s = "Hello World"; bool stringExists = s.Contains("ello"); //stringExists =true as the string contains the substring Section 11.14: Getting a char at specific index and enumerating the string You can use the Substring method to get any number of characters from a string at any given location. However, if you only want a single character, you can use the string indexer to get a single character at any given index like you GoalKicker.com – C# Notes for Professionals 59 do with an array: string s = "hello"; char c = s; //Returns 'e' Notice that the return type is char, unlike the Substring method which returns a string type. You can also use the indexer to iterate through the characters of the string: string s = "hello"; foreach (char c in s) Console.WriteLine(c); Section 11.15: Joining an array of strings into a new one var parts = new[] { "Foo", "Bar", "Fizz", "Buzz"}; var joined = string.Join(", ", parts); //joined = "Foo, Bar, Fizz, Buzz" Section 11.16: Replacing a string within a string Using the System.String.Replace method, you can replace part of a string with another string. string s = "Hello World"; s = s.Replace("World", "Universe"); // s = "Hello Universe" All the occurrences of the search string are replaced. This method can also be used to remove part of a string, using the String.Empty field: string s = "Hello World"; s = s.Replace("ell", String.Empty); // s = "Ho World" Section 11.17: Changing the case of characters within a String The System.String class supports a number of methods to convert between uppercase and lowercase characters in a string. System.String.ToLowerInvariant is used to return a String object converted to lowercase. System.String.ToUpperInvariant is used to return a String object converted to uppercase. Note: The reason to use the invariant versions of these methods is to prevent producing unexpected culture- specific letters. This is explained here in detail. Example: string s = "My String"; GoalKicker.com – C# Notes for Professionals 60 s = s.ToLowerInvariant(); // "my string" s = s.ToUpperInvariant(); // "MY STRING" Note that you can choose to specify a specific Culture when converting to lowercase and uppercase by using the String.ToLower(CultureInfo) and String.ToUpper(CultureInfo) methods accordingly. Section 11.18: Concatenate an array of strings into a single string The System.String.Join method allows to concatenate all elements in a string array, using a specified separator between each element: string[] words = {"One", "Two", "Three", "Four"}; string singleString = String.Join(",", words); // singleString = "One,Two,Three,Four" Section 11.19: String Concatenation String Concatenation can be done by using the System.String.Concat method, or (much easier) using the + operator: string first = "Hello "; string second = "World"; string concat = first + second; // concat = "Hello World" concat = String.Concat(first, second); // concat = "Hello World" In C# 6 this can be done as follows: string concat = $"{first},{second}"; GoalKicker.com – C# Notes for Professionals 61 Chapter 12: String.Format Parameter Details format A composite format string, which defines the way args should be combined into a string. A sequence of objects to be combined into a string. Since this uses a params argument, you can either args use a comma-separated list of arguments or an actual object array. A collection of ways of formatting objects to strings. Typical values include CultureInfo.InvariantCulture provider and CultureInfo.CurrentCulture. The Format methods are a set of overloads in the System.String class used to create strings that combine objects into specific string representations. This information can be applied to String.Format, various WriteLine methods as well as other methods in the.NET framework. Section 12.1: Since C# 6.0 Version ≥ 6.0 Since C# 6.0 it is possible to use string interpolation in place of String.Format. string name = "John"; string lastname = "Doe"; Console.WriteLine($"Hello {name} {lastname}!"); Hello John Doe! More examples for this under the topic C# 6.0 features: String interpolation. Section 12.2: Places where String.Format is 'embedded' in the framework There are several places where you can use String.Format indirectly: The secret is to look for the overload with the signature string format, params object[] args, e.g.: Console.WriteLine(String.Format("{0} - {1}", name, value)); Can be replaced with shorter version: Console.WriteLine("{0} - {1}", name, value); There are other methods which also use String.Formate.g.: Debug.WriteLine(); // and Print() StringBuilder.AppendFormat(); Section 12.3: Create a custom format provider public class CustomFormat : IFormatProvider, ICustomFormatter { public string Format(string format, object arg, IFormatProvider formatProvider) { if (!this.Equals(formatProvider)) GoalKicker.com – C# Notes for Professionals 62 { return null; } if (format == "Reverse") { return String.Join("", arg.ToString().Reverse()); } return arg.ToString(); } public object GetFormat(Type formatType) { return formatType==typeof(ICustomFormatter) ? this:null; } } Usage: String.Format(new CustomFormat(), "-> {0:Reverse} {0,-5}{1,-5}{0,5}{1,5}{0,10:#,#} System.Exception: Exception of type 'System.Exception' was thrown. [...] System.TypeInitializationException: The type initializer for 'Animal' threw an exception. ---> System.Exception: Exception of type 'System.Exception' was thrown. where you can see that the actual constructor is only executed once, and the exception is re-used. Section 39.9: Constructor and Property Initialization Shall the property value's assignment be executed before or after the class' constructor? public class TestClass { public int TestProperty { get; set; } = 2; public TestClass() { if (TestProperty == 1) GoalKicker.com – C# Notes for Professionals 158 { Console.WriteLine("Shall this be executed?"); } if (TestProperty == 2) { Console.WriteLine("Or shall this be executed"); } } } var testInstance = new TestClass() { TestProperty = 1 }; In the example above, shall the TestProperty value be 1 in the class' constructor or after the class constructor? Assigning property values in the instance creation like this: var testInstance = new TestClass() {TestProperty = 1}; Will be executed after the constructor is run. However, initializing the property value in the class' property in C# 6.0 like this: public class TestClass { public int TestProperty { get; set; } = 2; public TestClass() { } } will be done before the constructor is run. Combining the two concepts above in a single example: public class TestClass { public int TestProperty { get; set; } = 2; public TestClass() { if (TestProperty == 1) { Console.WriteLine("Shall this be executed?"); } if (TestProperty == 2) { Console.WriteLine("Or shall this be executed"); } } } static void Main(string[] args) { var testInstance = new TestClass() { TestProperty = 1 }; Console.WriteLine(testInstance.TestProperty); //resulting in 1 } GoalKicker.com – C# Notes for Professionals 159 Final result: "Or shall this be executed" "1" Explanation: The TestProperty value will first be assigned as 2, then the TestClass constructor will be run, resulting in printing of "Or shall this be executed" And then the TestProperty will be assigned as 1 due to new TestClass() { TestProperty = 1 }, making the final value for the TestProperty printed by Console.WriteLine(testInstance.TestProperty) to be "1" Section 39.10: Generic Static Constructors If the type on which the static constructor is declared is generic, the static constructor will be called once for each unique combination of generic arguments. class Animal { static Animal() { Console.WriteLine(typeof(T).FullName); } public static void Yawn() { } } Animal.Yawn(); Animal.Yawn(); This will output: System.Object System.String See also How do static constructors for generic types work ? Section 39.11: Calling virtual methods in constructor Unlike C++ in C# you can call a virtual method from class constructor (OK, you can also in C++ but behavior at first is surprising). For example: abstract class Base { protected Base() { _obj = CreateAnother(); } GoalKicker.com – C# Notes for Professionals 160 protected virtual AnotherBase CreateAnother() { return new AnotherBase(); } private readonly AnotherBase _obj; } sealed class Derived : Base { public Derived() { } protected override AnotherBase CreateAnother() { return new AnotherDerived(); } } var test = new Derived(); // test._obj is AnotherDerived If you come from a C++ background this is surprising, base class constructor already sees derived class virtual method table! Be careful: derived class may not been fully initialized yet (its constructor will be executed after base class constructor) and this technique is dangerous (there is also a StyleCop warning for this). Usually this is regarded as bad practice. GoalKicker.com – C# Notes for Professionals 161 Chapter 40: Access Modifiers Section 40.1: Access Modifiers Diagrams Here are all access modifiers in venn diagrams, from more limiting to more accessible: Access Modifier Diagram private internal protected protected internal GoalKicker.com – C# Notes for Professionals 162 public Below you could find more information. Section 40.2: public The public keyword makes a class (including nested classes), property, method or field available to every consumer: public class Foo() { public string SomeProperty { get; set; } public class Baz { public int Value { get; set; } } } public class Bar() { public Bar() { var myInstance = new Foo(); var someValue = foo.SomeProperty; var myNestedInstance = new Foo.Baz(); var otherValue = myNestedInstance.Value; } } Section 40.3: private The private keyword marks properties, methods, fields and nested classes for use inside the class only: public class Foo() { private string someProperty { get; set; } private class Baz { public string Value { get; set; } } public void Do() { var baz = new Baz { Value = 42 }; } } GoalKicker.com – C# Notes for Professionals 163 public class Bar() { public Bar() { var myInstance = new Foo(); // Compile Error - not accessible due to private modifier var someValue = foo.someProperty; // Compile Error - not accessible due to private modifier var baz = new Foo.Baz(); } } Section 40.4: protected internal The protected internal keyword marks field, methods, properties and nested classes for use inside the same assembly or derived classes in another assembly: Assembly 1 public class Foo { public string MyPublicProperty { get; set; } protected internal string MyProtectedInternalProperty { get; set; } protected internal class MyProtectedInternalNestedClass { private string blah; public int N { get; set; } } } public class Bar { void MyMethod1() { Foo foo = new Foo(); var myPublicProperty = foo.MyPublicProperty; var myProtectedInternalProperty = foo.MyProtectedInternalProperty; var myProtectedInternalNestedInstance = new Foo.MyProtectedInternalNestedClass(); } } Assembly 2 public class Baz : Foo { void MyMethod1() { var myPublicProperty = MyPublicProperty; var myProtectedInternalProperty = MyProtectedInternalProperty; var thing = new MyProtectedInternalNestedClass(); } void MyMethod2() GoalKicker.com – C# Notes for Professionals 164 { Foo foo = new Foo(); var myPublicProperty = foo.MyPublicProperty; // Compile Error var myProtectedInternalProperty = foo.MyProtectedInternalProperty; // Compile Error var myProtectedInternalNestedInstance = new Foo.MyProtectedInternalNestedClass(); } } public class Qux { void MyMethod1() { Baz baz = new Baz(); var myPublicProperty = baz.MyPublicProperty; // Compile Error var myProtectedInternalProperty = baz.MyProtectedInternalProperty; // Compile Error var myProtectedInternalNestedInstance = new Baz.MyProtectedInternalNestedClass(); } void MyMethod2() { Foo foo = new Foo(); var myPublicProperty = foo.MyPublicProperty; //Compile Error var myProtectedInternalProperty = foo.MyProtectedInternalProperty; // Compile Error var myProtectedInternalNestedInstance = new Foo.MyProtectedInternalNestedClass(); } } Section 40.5: internal The internal keyword makes a class (including nested classes), property, method or field available to every consumer in the same assembly: internal class Foo { internal string SomeProperty {get; set;} } internal class Bar { var myInstance = new Foo(); internal string SomeField = foo.SomeProperty; internal class Baz { private string blah; public int N { get; set; } } GoalKicker.com – C# Notes for Professionals 165 } This can be broken to allow a testing assembly to access the code via adding code to AssemblyInfo.cs file: using System.Runtime.CompilerServices; [assembly:InternalsVisibleTo("MyTests")] Section 40.6: protected The protected keyword marks field, methods properties and nested classes for use inside the same class and derived classes only: public class Foo() { protected void SomeFooMethod() { //do something } protected class Thing { private string blah; public int N { get; set; } } } public class Bar() : Foo { private void someBarMethod() { SomeFooMethod(); // inside derived class var thing = new Thing(); // can use nested class } } public class Baz() { private void someBazMethod() { var foo = new Foo(); foo.SomeFooMethod(); //not accessible due to protected modifier } } GoalKicker.com – C# Notes for Professionals 166 Chapter 41: Interfaces Section 41.1: Implementing an interface An interface is used to enforce the presence of a method in any class that 'implements' it. The interface is defined with the keyword interface and a class can 'implement' it by adding : InterfaceName after the class name. A class can implement multiple interfaces by separating each interface with a comma. : InterfaceName, ISecondInterface public interface INoiseMaker { string MakeNoise(); } public class Cat : INoiseMaker { public string MakeNoise() { return "Nyan"; } } public class Dog : INoiseMaker { public string MakeNoise() { return "Woof"; } } Because they implement INoiseMaker, both cat and dog are required to include the string MakeNoise() method and will fail to compile without it. Section 41.2: Explicit interface implementation Explicit interface implementation is necessary when you implement multiple interfaces who define a common method, but different implementations are required depending on which interface is being used to call the method (note that you don't need explicit implementations if multiple interfaces share the same method and a common implementation is possible). interface IChauffeur { string Drive(); } interface IGolfPlayer { string Drive(); } class GolfingChauffeur : IChauffeur, IGolfPlayer { public string Drive() { return "Vroom!"; } GoalKicker.com – C# Notes for Professionals 167 string IGolfPlayer.Drive() { return "Took a swing..."; } } GolfingChauffeur obj = new GolfingChauffeur(); IChauffeur chauffeur = obj; IGolfPlayer golfer = obj; Console.WriteLine(obj.Drive()); // Vroom! Console.WriteLine(chauffeur.Drive()); // Vroom! Console.WriteLine(golfer.Drive()); // Took a swing... The implementation cannot be called from anywhere else except by using the interface: public class Golfer : IGolfPlayer { string IGolfPlayer.Drive() { return "Swinging hard..."; } public void Swing() { Drive(); // Compiler error: No such method } } Due to this, it may be advantageous to put complex implementation code of an explicitly implemented interface in a separate, private method. An explicit interface implementation can of course only be used for methods that actually exist for that interface: public class ProGolfer : IGolfPlayer { string IGolfPlayer.Swear() // Error { return "The ball is in the pit"; } } Similarly, using an explicit interface implementation without declaring that interface on the class causes an error, too. Hint: Implementing interfaces explicitly can also be used to avoid dead code. When a method is no longer needed and gets removed from the interface, the compiler will complain about each still existing implementation. Note: Programmers expect the contract to be the same regardless of the context of the type and explicit implementation should not expose different behavior when called. So unlike the example above, IGolfPlayer.Drive and Drive should do the same thing when possible. GoalKicker.com – C# Notes for Professionals 168 Section 41.3: Interface Basics An Interface's function known as a "contract" of functionality. It means that it declares properties and methods but it doesn't implement them. So unlike classes Interfaces: Can't be instantiated Can't have any functionality Can only contain methods * (Properties and Events are methods internally) Inheriting an interface is called "Implementing" You can inherit from 1 class, but you can "Implement" multiple Interfaces public interface ICanDoThis{ void TheThingICanDo(); int SomeValueProperty { get; set; } } Things to notice: The "I" prefix is a naming convention used for interfaces. The function body is replaced with a semicolon ";". Properties are also allowed because internally they are also methods public class MyClass : ICanDoThis { public void TheThingICanDo(){ // do the thing } public int SomeValueProperty { get; set; } public int SomeValueNotImplemtingAnything { get; set; } }. ICanDoThis obj = new MyClass(); // ok obj.TheThingICanDo(); // ok obj.SomeValueProperty = 5; // Error, this member doesn't exist in the interface obj.SomeValueNotImplemtingAnything = 5; // in order to access the property in the class you must "down cast" it ((MyClass)obj).SomeValueNotImplemtingAnything = 5; // ok This is especially useful when you're working with UI frameworks such as WinForms or WPF because it's mandatory to inherit from a base class to create user control and you loose the ability to create abstraction over different control types. An example? Coming up: public class MyTextBlock : TextBlock { public void SetText(string str){ this.Text = str; } GoalKicker.com – C# Notes for Professionals 169 } public class MyButton : Button { public void SetText(string str){ this.Content = str; } } The problem proposed is that both contain some concept of "Text" but the property names differ. And you can't create create a abstract base class because they have a mandatory inheritance to 2 different classes. An interface can alleviate that public interface ITextControl{ void SetText(string str); } public class MyTextBlock : TextBlock, ITextControl { public void SetText(string str){ this.Text = str; } } public class MyButton : Button, ITextControl { public void SetText(string str){ this.Content = str; } public int Clicks { get; set; } } Now MyButton and MyTextBlock is interchangeable. var controls = new List{ new MyTextBlock(), new MyButton() }; foreach(var ctrl in controls){ ctrl.SetText("This text will be applied to both controls despite them being different"); // Compiler Error, no such member in interface ctrl.Clicks = 0; // Runtime Error because 1 class is in fact not a button which makes this cast invalid ((MyButton)ctrl).Clicks = 0; var button = ctrl as MyButton; if(button != null) button.Clicks = 0; // no errors } GoalKicker.com – C# Notes for Professionals 170 Section 41.4: IComparable as an Example of Implementing an Interface Interfaces can seem abstract until you seem them in practice. The IComparable and IComparable are great examples of why interfaces can be helpful to us. Let's say that in a program for a online store, we have a variety of items you can buy. Each item has a name, an ID number, and a price. public class Item { public string name; // though public variables are generally bad practice, public int idNumber; // to keep this example simple we will use them instead public decimal price; // of a property. // body omitted for brevity } We have our Items stored inside of a List, and in our program somewhere, we want to sort our list by ID number from smallest to largest. Instead of writing our own sorting algorithm, we can instead use the Sort() method that List already has. However, as our Item class is right now, there is no way for the List to understand what order to sort the list. Here is where the IComparable interface comes in. To correctly implement the CompareTo method, CompareTo should return a positive number if the parameter is "less than" the current one, zero if they are equal, and a negative number if the parameter is "greater than". Item apple = new Item(); apple.idNumber = 15; Item banana = new Item(); banana.idNumber = 4; Item cow = new Item(); cow.idNumber = 15; Item diamond = new Item(); diamond.idNumber = 18; Console.WriteLine(apple.CompareTo(banana)); // 11 Console.WriteLine(apple.CompareTo(cow)); // 0 Console.WriteLine(apple.CompareTo(diamond)); // -3 Here's the example Item's implementation of the interface: public class Item : IComparable { private string name; private int idNumber; private decimal price; public int CompareTo(Item otherItem) { return (this.idNumber - otherItem.idNumber); } // rest of code omitted for brevity } GoalKicker.com – C# Notes for Professionals 171 On a surface level, the CompareTo method in our item simply returns the difference in their ID numbers, but what does the above do in practice? Now, when we call Sort() on a List object, the List will automatically call the Item's CompareTo method when it needs to determine what order to put objects in. Furthermore, besides List, any other objects that need the ability to compare two objects will work with the Item because we have defined the ability for two different Items to be compared with one another. Section 41.5: Implementing multiple interfaces public interface IAnimal { string Name { get; set; } } public interface INoiseMaker { string MakeNoise(); } public class Cat : IAnimal, INoiseMaker { public Cat() { Name = "Cat"; } public string Name { get; set; } public string MakeNoise() { return "Nyan"; } } Section 41.6: Why we use interfaces An interface is a definition of a contract between the user of the interface and the class that implement it. One way to think of an interface is as a declaration that an object can perform certain functions. Let's say that we define an interface IShape to represent different type of shapes, we expect a shape to have an area, so we will define a method to force the interface implementations to return their area : public interface IShape { double ComputeArea(); } Let's that we have the following two shapes : a Rectangle and a Circle public class Rectangle : IShape { private double length; private double width; public Rectangle(double length, double width) { this.length = length; GoalKicker.com – C# Notes for Professionals 172 this.width = width; } public double ComputeArea() { return length * width; } } public class Circle : IShape { private double radius; public Circle(double radius) { this.radius = radius; } public double ComputeArea() { return Math.Pow(radius, 2.0) * Math.PI; } } Each one of them have its own definition of its area, but both of them are shapes. So it's only logical to see them as IShape in our program : private static void Main(string[] args) { var shapes = new List() { new Rectangle(5, 10), new Circle(5) }; ComputeArea(shapes); Console.ReadKey(); } private static void ComputeArea(IEnumerable shapes) { foreach (shape in shapes) { Console.WriteLine("Area: {0:N}, shape.ComputeArea()); } } // Output: // Area : 50.00 // Area : 78.54 Section 41.7: "Hiding" members with Explicit Implementation Don't you hate it when interfaces pollute you class with too many members you don't even care about? Well I got a solution! Explicit Implementations public interface IMessageService { void OnMessageRecieve(); void SendMessage(); string Result { get; set; } int Encoding { get; set; } // yadda yadda } GoalKicker.com – C# Notes for Professionals 173 Normally you'd implement the class like this. public class MyObjectWithMessages : IMessageService { public void OnMessageRecieve(){ } public void SendMessage(){ } public string Result { get; set; } public int Encoding { get; set; } } Every member is public. var obj = new MyObjectWithMessages(); // why would i want to call this function? obj.OnMessageRecieve(); Answer: I don't. So neither should it be declared public but simply declaring the members as private will make the compiler throw an error The solution is to use explicit implementation: public class MyObjectWithMessages : IMessageService{ void IMessageService.OnMessageRecieve() { } void IMessageService.SendMessage() { } string IMessageService.Result { get; set; } int IMessageService.Encoding { get; set; } } So now you have implemented the members as required and they won't expose any members in as public. var obj = new MyObjectWithMessages(); obj.OnMessageRecieve(); If you seriously still want to access the member even though is explicitly implement all you have to do is cast the object to the interface and you good to go. ((IMessageService)obj).OnMessageRecieve(); GoalKicker.com – C# Notes for Professionals 174 Chapter 42: Static Classes Section 42.1: Static Classes The "static" keyword when referring to a class has three effects: 1. You cannot create an instance of a static class (this even removes the default constructor) 2. All properties and methods in the class must be static as well. 3. A static class is a sealed class, meaning it cannot be inherited. public static class Foo { //Notice there is no constructor as this cannot be an instance public static int Counter { get; set; } public static int GetCount() { return Counter; } } public class Program { static void Main(string[] args) { Foo.Counter++; Console.WriteLine(Foo.GetCount()); //this will print 1 //var foo1 = new Foo(); //this line would break the code as the Foo class does not have a constructor } } Section 42.2: Static class lifetime A static class is lazily initialized on member access and lives for the duration of the application domain. void Main() { Console.WriteLine("Static classes are lazily initialized"); Console.WriteLine("The static constructor is only invoked when the class is first accessed"); Foo.SayHi(); Console.WriteLine("Reflecting on a type won't trigger its static.ctor"); var barType = typeof(Bar); Console.WriteLine("However, you can manually trigger it with System.Runtime.CompilerServices.RuntimeHelpers"); RuntimeHelpers.RunClassConstructor(barType.TypeHandle); } // Define other methods and classes here public static class Foo { static Foo() { Console.WriteLine("static Foo.ctor"); } public static void SayHi() GoalKicker.com – C# Notes for Professionals 175 { Console.WriteLine("Foo: Hi"); } } public static class Bar { static Bar() { Console.WriteLine("static Bar.ctor"); } } Section 42.3: Static keyword The static keyword means 2 things: 1. This value does not change from object to object but rather changes on a class as a whole 2. Static properties and methods don't require an instance. public class Foo { public Foo{ Counter++; NonStaticCounter++; } public static int Counter { get; set; } public int NonStaticCounter { get; set; } } public class Program { static void Main(string[] args) { //Create an instance var foo1 = new Foo(); Console.WriteLine(foo1.NonStaticCounter); //this will print "1" //Notice this next call doesn't access the instance but calls by the class name. Console.WriteLine(Foo.Counter); //this will also print "1" //Create a second instance var foo2 = new Foo(); Console.WriteLine(foo2.NonStaticCounter); //this will print "1" Console.WriteLine(Foo.Counter); //this will now print "2" //The static property incremented on both instances and can persist for the whole class } } GoalKicker.com – C# Notes for Professionals 176 Chapter 43: Singleton Implementation Section 43.1: Statically Initialized Singleton public class Singleton { private readonly static Singleton instance = new Singleton(); private Singleton() { } public static Singleton Instance => instance; } This implementation is thread-safe because in this case instance object is initialized in the static constructor. The CLR already ensures that all static constructors are executed thread-safe. Mutating instance is not a thread-safe operation, therefore the readonly attribute guarantees immutability after initialization. Section 43.2: Lazy, thread-safe Singleton (using Lazy).Net 4.0 type Lazy guarantees thread-safe object initialization, so this type could be used to make Singletons. public class LazySingleton { private static readonly Lazy _instance = new Lazy(() => new LazySingleton()); public static LazySingleton Instance { get { return _instance.Value; } } private LazySingleton() { } } Using Lazy will make sure that the object is only instantiated when it is used somewhere in the calling code. A simple usage will be like: using System; public class Program { public static void Main() { var instance = LazySingleton.Instance; } } Live Demo on.NET Fiddle Section 43.3: Lazy, thread-safe Singleton (using Double Checked Locking) This thread-safe version of a singleton was necessary in the early versions of.NET where static initialization was not guaranteed to be thread-safe. In more modern versions of the framework a statically initialized singleton is GoalKicker.com – C# Notes for Professionals 177 usually preferred because it is very easy to make implementation mistakes in the following pattern. public sealed class ThreadSafeSingleton { private static volatile ThreadSafeSingleton instance; private static object lockObject = new Object(); private ThreadSafeSingleton() { } public static ThreadSafeSingleton Instance { get { if (instance == null) { lock (lockObject) { if (instance == null) { instance = new ThreadSafeSingleton(); } } } return instance; } } } Notice that the if (instance == null) check is done twice: once before the lock is acquired, and once afterwards. This implementation would still be thread-safe even without the first null check. However, that would mean that a lock would be acquired every time the instance is requested, and that would cause performance to suffer. The first null check is added so that the lock is not acquired unless it's necessary. The second null check makes sure that only the first thread to acquire the lock then creates the instance. The other threads will find the instance to be populated and ski