Dot NET Framework Notes
Dot NET Framework Notes
NET
.NET Framework
WWW.LEARNLONER.COM
Chapter 1: Getting started with .NET
Framework
.NET
Version Release Date
1.0 2002-02-13
1.1 2003-04-24
2.0 2005-11-07
3.0 2006-11-06
3.5 2007-11-19
3.5 SP1 2008-08-11
4.0 2010-04-12
4.5 2012-08-15
4.5.1 2013-10-17
4.5.2 2014-05-05
4.6 2015-07-20
4.6.1 2015-11-17
4.6.2 2016-08-02
4.7 2017-04-05
4.7.1 2017-10-17
Compact Framework
Version Release Date
1.0 2000-01-01
2.0 2005-10-01
3.5 2007-11-19
3.7 2009-01-01
3.9 2013-06-01
Micro Framework
Version Release Date
4.2 2011-10-04
4.3 2012-12-04
4.4 2015-10-20
class Program
{
// The Main() function is the first function to be executed in a program
static void Main()
{
// Write the string "Hello World to the standard out
Console.WriteLine("Hello World");
}
}
Console.WriteLine has several overloads. In this case, the string "Hello World" is the parameter, and it will output
the "Hello World" to the standard out stream during execution. Other overloads may call the .ToString of the
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 2
argument before writing to the stream. See the .NET Framework Documentation for more information.
Introduction to C#
[<EntryPoint>]
let main argv =
printfn "Hello World"
0
Introduction to F#
Module Program
Public Sub Main()
Console.WriteLine("Hello World")
End Sub
End Module
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 3
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
}
Introduction to PowerShell
import clr
from System import Console
Console.WriteLine("Hello World")
interface
type
App = class
public
class method Main(args: array of String);
end;
implementation
end.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 4
Chapter 2: Strings
Section 2.1: Count characters
If you need to count characters then, for the reasons explained in Remarks section, you can't simply use Length
property because it's the length of the array of System.Char which are not characters but code-units (not Unicode
code-points nor graphemes). Correct code is then:
A small optimization may rewrite EnumerateCharacters() extension method specifically for this purpose:
int count = 0;
var enumerator = StringInfo.GetTextElementEnumerator(text);
while (enumerator.MoveNext())
++count;
return count;
}
}
One step further is to count occurrences of each character, if performance aren't an issue you may simply do it
like this (in this example regardless of case):
Conversions are performed by classes derived from System.Text.Encoder and System.Text.Decoder which,
together, can convert to/from another encoding (from a byte X encoded array byte[] to an UTF-16 encoded
System.String and vice-versa).
Because the encoder/decoder usually works very close to each other they're grouped together in a class derived
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 5
from System.Text.Encoding, derived classes offer conversions to/from popular encodings (UTF-8, UTF-16 and so
on).
Examples:
Convert a string to UTF-8
byte[] data = Encoding.UTF8.GetBytes("This is my text");
This code will read content of an UTF-8 encoded text file and save it back encoded as UTF-16. Note that this code is
not optimal if file is big because it will read all its content into memory:
As you may know string is just an array of characters. But if you think that strings equality check and comparison
is made character by character, you are mistaken. This operation is culture specific (see Remarks below): some
character sequences can be treated as equal depending on the culture.
Think twice before short circuiting equality check by comparing Length properties of two strings!
Use overloads of String.Equals method which accept additional StringComparison enumeration value, if you
need to change default behavior.
Note that string comparison (in contrast to character comparison which is culture invariant) must always be
performed according to rules to a specific culture.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 6
number of graphemes not the number of code-units):
if (!enumerator.MoveNext())
yield break;
}
}
The result of this method is also extensively used by debugging tools. If, for some reason, you do not want to
override this method, but want to customize how debugger shows the value of your type, use DebuggerDisplay
Attribute (MSDN):
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 7
Section 2.8: Immutability of strings
Strings are immutable. You just cannot change existing string. Any operation on the string crates a new instance of
the string having new value. It means that if you need to replace a single character in a very long string, memory will
be allocated for a new value.
If you need to perform many operations with string value, use StringBuilder class which is designed for efficient
strings manipulation:
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 8
Chapter 3: DateTime parsing
Section 3.1: ParseExact
var dateString = "2015-11-24";
11/24/2015 12:00:00 AM
Note that passing CultureInfo.CurrentCulture as the third parameter is identical to passing null. Or, you can
pass a specific culture.
Format Strings
Input string can be in any format that matches the format string
11/24/2015 12:00:00 AM
Any characters that are not format specifiers are treated as literals
11/24/2015 12:00:00 AM
11/24/2015 11:01:30 AM
Note that the month and minute values were parsed into the wrong destinations.
Exceptions
ArgumentNullException
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 9
var date = DateTime.ParseExact(null, "yyyy-MM-dd", null);
var date = DateTime.ParseExact("2015-11-24", null, null);
FormatException
// Format strings must match the input exactly* (see next section)
var date = DateTime.ParseExact("2015-11-24", "d", null); // Expects 11/24/2015 or 24/11/2015 for
most cultures
If the parse fails, the variable passed as the out parameter is set to the default value, DateTime.MinValue.
DateTime parsedValue;
This method attempts to parse the input string based on the system regional settings and known formats such as
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 10
ISO 8601 and other common formats.
Since this method does not accept culture info, it uses the system locale. This can lead to unexpected results.
False
False
True
Note that if you are in the US, you might be surprised that the parsed result is November 10, not October 11.
Unlike its sibling method, this overload allows a specific culture and style(s) to be specified. Passing null for the
IFormatProvider parameter uses the system culture.
Exceptions
Note that it is possible for this method to throw an exception under certain conditions. These relate to the
parameters introduced for this overload: IFormatProvider and DateTimeStyles.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 11
Section 3.3: TryParseExact
This method behaves as a combination of TryParse and ParseExact: It allows custom format(s) to be specified, and
returns a Boolean result indicating success or failure rather than throwing an exception if the parse fails.
This overload attempts to parse the input string against a specific format. The input string must match that format
in order to be parsed.
This overload attempts to parse the input string against an array of formats. The input string must match at least
one format in order to be parsed.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 12
Chapter 4: Dictionaries
Section 4.1: Initializing a Dictionary with a Collection Initializer
// Translates to `dict.Add(1, "First")` etc.
var dict = new Dictionary<int, string>()
{
{ 1, "First" },
{ 2, "Second" },
{ 3, "Third" }
};
// To safely add items (check to ensure item does not already exist - would throw)
if(!dict.ContainsKey(3))
{
dict.Add(3, "Third");
}
Alternatively they can be added/set via the an indexer. (An indexer internally looks like a property, having a get and
set, but takes a parameter of any type which is specified between the brackets) :
Unlike the Add method which throws an exception, if a key is already contained in the dictionary, the indexer just
replaces the existing value.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 13
{ 2, "Second" },
{ 3, "Third" }
};
You may want to read the value for the entry with key 1. If key doesn't exist getting a value will throw
KeyNotFoundException, so you may want to first check for that with ContainsKey:
if (dict.ContainsKey(1))
Console.WriteLine(dict[1]);
This has one disadvantage: you will search through your dictionary twice (once to check for existence and one to
read the value). For a large dictionary this can impact performance. Fortunately both operations can be performed
together:
string value;
if (dict.TryGetValue(1, out value))
Console.WriteLine(value);
using System;
using System.Collections.Generic;
using System.Linq;
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 14
Dictionary<int, string> dict = new Dictionary<int, string>();
foreach(KeyValuePair<int, string> kvp in dict)
{
Console.WriteLine("Key : " + kvp.Key.ToString() + ", Value : " + kvp.Value);
}
Using Keys
Using Values
Represents a thread-safe collection of key/value pairs that can be accessed by multiple threads
concurrently.
Creating an instance
Creating an instance works pretty much the same way as with Dictionary<TKey, TValue>, e.g.:
Adding or Updating
You might be surprised, that there is no Add method, but instead there is AddOrUpdate with 2 overloads:
(1) AddOrUpdate(TKey key, TValue, Func<TKey, TValue, TValue> addValue) - Adds a key/value pair if the key does
not already exist, or updates a key/value pair by using the specified function if the key already exists.
(2) AddOrUpdate(TKey key, Func<TKey, TValue> addValue, Func<TKey, TValue, TValue> updateValueFactory)
- Uses the specified functions to add a key/value pair to the if the key does not already exist, or to update a key/value pair
if the key already exists.
Adding or updating a value, no matter what was the value if it was already present for given key (1):
Adding or updating a value, but now altering the value in update, based on the previous value (1):
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 15
Using the overload (2) we can also add new value using a factory:
string addedValue3 = dict.AddOrUpdate(1, (key) => key == 1 ? "First" : "Not First", (updateKey,
valueOld) => $"{valueOld} Updated");
Getting value
There are two mehod overloads, that will get or add a value in a thread-safe manner.
Get value with key 2, or add value "Second" if the key is not present:
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 16
Use the Remove method to remove a key and its associated value.
Executing this code removes the key 2 and it's value from the dictionary. Remove returns a boolean value indicating
whether the specified key was found and removed from the dictionary. If the key does not exist in the dictionary,
nothing is removed from the dictionary, and false is returned (no exception is thrown).
It's incorrect to try and remove a key by setting the value for the key to null.
This will not remove the key. It will just replace the previous value with a value of null.
To remove all keys and values from a dictionary, use the Clear method.
dict.Clear();
After executing Clear the dictionary's Count will be 0, but the internal capacity remains unchanged.
if (dictionary.ContainsKey("C2"))
{
// exists
}
ConcurrentDictionary shines when it comes to instantly returning of existing keys from cache, mostly lock free, and
contending on a granular level. But what if the object creation is really expensive, outweighing the cost of context
switching, and some cache misses occur?
If the same key is requested from multiple threads, one of the objects resulting from colliding operations will be
eventually added to the collection, and the others will be thrown away, wasting the CPU resource to create the
object and memory resource to store the object temporarily. Other resources could be wasted as well. This is really
bad.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 17
Solution
We can combine ConcurrentDictionary<TKey, TValue> with Lazy<TValue>. The idea is that ConcurrentDictionary
GetOrAdd method can only return the value which was actually added to the collection. The loosing Lazy objects
could be wasted in this case too, but that's not much problem, as the Lazy object itself is relatively unexpensive. The
Value property of the losing Lazy is never requested, because we are smart to only request the Value property of
the one actually added to the collection - the one returned from the GetOrAdd method:
Caching of XmlSerializer objects can be particularly expensive, and there is a lot of contention at the application
startup too. And there is more to this: if those are custom serializers, there will be a memory leak too for the rest of
the process lifecycle. The only benefit of the ConcurrentDictionary in this case is that for the rest of the process
lifecycle there will be no locks, but application startup and memory usage would be inacceptable. This is a job for
our ConcurrentDictionary, augmented with Lazy:
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 18
Chapter 5: Collections
Section 5.1: Using collection initializers
Some collection types can be initialized at the declaration time. For example, the following statement creates and
initializes the numbers with some integers:
Internally, the C# compiler actually converts this initialization to a series of calls to the Add method. Consequently,
you can use this syntax only for collections that actually support the Add method.
For complex collections such as the Dictionary<TKey, TValue> class, that take key/value pairs, you can specify
each key/value pair as an anonymous type in the initializer list.
The first item in each pair is the key, and the second is the value.
using System.Collections.Generic;
string value;
value = stack.Pop(); // return Ringo
value = stack.Pop(); // return George
value = stack.Pop(); // return Paul
value = stack.Pop(); // return John
There is a non generic version of the type, which works with objects.
using System.Collections;
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 19
And a code sample of non generic stack:
object value;
value = stack.Pop(); // return Product (Product type)
value = stack.Pop(); // return true (bool)
value = stack.Pop(); // return 1d (double)
value = stack.Pop(); // return 5 (int)
value = stack.Pop(); // return Hello World (string)
There is also a method called Peek() which returns the last element added but without removing it from the Stack.
It is possible to iterate on the elements on the stack and it will respect the order of the stack (LIFO).
50
40
30
20
10
Here we have a Class with no constructor with two properties: Name and a nullable boolean property Selected. If we
wanted to initialize a List<Model>, there are a few different ways to execute this.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 20
var SelectedEmployees = new List<Model>
{
new Model() {Name = "Item1", Selected = true},
new Model() {Name = "Item2", Selected = false},
new Model() {Name = "Item3", Selected = false},
new Model() {Name = "Item4"}
};
Here, we are creating several new instances of our Model class, and initializing them with data. What if we added a
constructor?
Notice we reverted the constructor on the Model class to simplify the example a little bit.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 21
},
new ExtendedModel()
{
BaseModel = new Model { Name = "Random"},
BirthDate = new DateTime(2015, 11, 23)
}
};
using System.Collections.Generic;
string dequeueValue;
dequeueValue = queue.Dequeue(); // return John
dequeueValue = queue.Dequeue(); // return Paul
dequeueValue = queue.Dequeue(); // return George
dequeueValue = queue.Dequeue(); // return Ringo
There is a non generic version of the type, which works with objects.
using System.Collections;
object dequeueValue;
dequeueValue = queue.Dequeue(); // return Hello World (string)
dequeueValue = queue.Dequeue(); // return 5 (int)
dequeueValue = queue.Dequeue(); // return 1d (double)
dequeueValue = queue.Dequeue(); // return true (bool)
dequeueValue = queue.Dequeue(); // return Product (Product type)
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 22
There is also a method called Peek() which returns the object at the beginning of the queue without removing it the
elements.
10
20
30
40
50
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 23
Chapter 6: ReadOnlyCollections
Section 6.1: Creating a ReadOnlyCollection
Using the Constructor
Using LINQ
Note
Typically, you want to maintain the source collection privately and allow public access to the ReadOnlyCollection.
While you could create a ReadOnlyCollection from an in-line list, you would be unable to modify the collection
after you created it.
If you find yourself doing this, you may want to consider using another data structure, such as an
ImmutableCollection.
View Demo
// The customer can't add or remove items, but they can change
// the price of an item, even though it is a ReadOnlyCollection
customerPreview.Last().Price = 0.25m;
View Demo
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 25
Chapter 7: Stack and Heap
Section 7.1: Value types in use
Value types simply contain a value.
All value types are derived from the System.ValueType class, and this includes most of the built in types.
When creating a new value type, the an area of memory called the stack is used.
The stack will grow accordingly, by the size the declared type. So for example, an int will always be allocated 32 bits
of memory on the stack. When the value type is no longer in scope, the space on the stack will be deallocated.
The code below demonstrates a value type being assigned to a new variable. A struct is being used as a convenient
way to create a custom value type (the System.ValueType class cannot be otherwise extended).
The important thing to understand is that when assigning a value type, the value itself copied to the new variable,
meaning we have two distinct instances of the object, that cannot affect each other.
struct PersonAsValueType
{
public string Name;
}
class Program
{
static void Main()
{
PersonAsValueType personA;
personA.Name = "Bob";
personA.Name = "Linda";
In addition to the memory space required for the instance itself, additional space is required to store the reference
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 26
itself, along with additional temporary information required by the .NET CLR.
The code below demonstrates a reference type being assigned to a new variable. In this instance, we are using a
class, all classes are reference types (even if static).
When a reference type is assigned to another variable, it is the reference to the object that is copied over, not the
value itself. This is an important distinction between value types and reference types.
The implications of this are that we now have two references to the same object.
Any changes to the values within that object will be reflected by both variables.
class PersonAsReferenceType
{
public string Name;
}
class Program
{
static void Main()
{
PersonAsReferenceType personA;
personA.Name = "Linda";
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 27
Chapter 8: LINQ
LINQ (Language Integrated Query) is an expression that retrieves data from a data source. LINQ simplifies this
situation by offering a consistent model for working with data across various kinds of data sources and formats. In
a LINQ query, you are always working with objects. You use the same basic coding patterns to query and transform
data in XML documents, SQL databases, ADO.NET Datasets, .NET collections, and any other format for which a
provider is available. LINQ can be used in C# and VB.
Lambda expressions passed to Enumerable.Select must return a single item. Lambda expressions passed to
Enumerable.SelectMany must produce a child sequence. This child sequence may contain a varying number of
elements for each element in the input sequence.
Example
class Invoice
{
public int Id { get; set; }
}
class Customer
{
public Invoice[] Invoices {get;set;}
}
Console.WriteLine(
string.Join(",", allInvoicesFromAllCustomers.Select(i => i.Id).ToArray()));
Output:
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 28
1,2,3,4,5,6
View Demo
Enumerable.SelectMany can also be achieved with a syntax-based query using two consecutive from clauses:
var allInvoicesFromAllCustomers
= from customer in customers
from invoice in customer.Invoices
select invoice;
Example
Output:
Foo,Fizz
View Demo
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 29
Section 8.4: GroupJoin
class Developer
{
public int Id { get; set; }
public string Name { get; set; }
}
class Project
{
public int DeveloperId { get; set; }
public string Name { get; set; }
}
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 30
//Foobuzz's projects: Hello World 3D, Super Fizzbuzz Maker
//Barfizz's projects: Citizen Kane - The action game, Pro Pong 2016
Console.WriteLine(string.Join(",", result));
//1, 2, 3, 4, 5, 7, 9
Console.WriteLine(string.Join(",", sums));
//11,22,33,44,55
Console.WriteLine(commaSeparatedElements); //1,2,3,4,5,
Console.WriteLine(commaSeparatedElements2.ToString()); //1,2,3,4,5,
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 31
var seedAndElements = elements.Select(n=>n.ToString());
var commaSeparatedElements4 = seedAndElements.Aggregate(
func: (aggregate, element) => $"{aggregate}{element},");
Console.WriteLine(commaSeparatedElements4); //12,3,4,5,
Console.WriteLine(string.Join(",", numbers5to10));
//5,6,7,8,9,10
Console.WriteLine(string.Join(",", numbers1to8));
//1,2,3,4,5,4,5,6,7,8
Note that duplicates are kept in the result. If this is undesirable, use Union instead.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 32
Console.WriteLine(allNumbersAreOdd); //False
Note that the All method functions by checking for the first element to evaluate as false according to the
predicate. Therefore, the method will return true for any predicate in the case that the set is empty:
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 33
Section 8.15: Distinct
var numbers = new[] {1, 1, 2, 2, 3, 3, 4, 4, 5, 5};
var distinctNumbers = numbers.Distinct();
Console.WriteLine(string.Join(",", distinctNumbers));
//1,2,3,4,5
This does not compile since ArrayList does not implement IEnumerable<T>:
Cast does not perform conversion casts. The following compiles but throws InvalidCastException at runtime:
// prints 1,2,3,4,5,6,7,8,9,10
Console.WriteLine(string.Join(",", Enumerable.Range(1, 10)));
// prints 10,11,12,13,14
Console.WriteLine(string.Join(",", Enumerable.Range(10, 5)));
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 34
Section 8.19: ThenBy
ThenBy can only be used after a OrderBy clause allowing to order using multiple criteria
/* output:
Hello
Hello
Hello
Hello
*/
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 35
};
//Foo,Bar,Fizz,Buzz
//2,4,3,1
//1,3,4,2
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 36
The following throws InvalidOperationException with message "Sequence contains no matching element":
The following throws InvalidOperationException since there is more than one element in the sequence:
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 37
Console.WriteLine(lastMissingWord); // null
//3,4,5
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 38
Section 8.33: Take
This method takes the first n elements from an enumerable.
//1,2,3
Console.WriteLine(string.Join(",", reversed.ToArray()));
//5,4,3,2,1
Console.WriteLine(string.Join(",", numbers.ToArray()));
//1,2,3,4
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 39
new {Population = 1000},
new {Population = 2000},
new {Population = 4000}
};
Group invoices by country, generating a new object with the number of record, total paid, and average paid
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 40
Will throw an ArgumentException if keySelector is not injective(returns a unique value for each member of the
source collection.) There are overloads which allow one to specify the value to be stored as well as the key.
Specifying just a key selector function will create a Dictionary<TKey,TVal> with TKey the return Type of the key
selector, TVal the original object Type, and the original object as the stored value.
Console.WriteLine(personsById[1].Name); //Fizz
Console.WriteLine(personsById[2].Name); //Buzz
Specifying a value selector function as well will create a Dictionary<TKey,TVal> with TKey still the return type of
the key selector, but TVal now the return type of the value selector function, and the returned value as the stored
value.
Console.WriteLine(namesById[3]); //Foo
Console.WriteLine(namesById[4]); //Bar
As stated above, the keys returned by the key selector must be unique. The following will throw an exception.
If a unique key can not be given for the source collection, consider using ToLookup instead. On the surface,
ToLookup behaves similarly to ToDictionary, however, in the resulting Lookup each key is paired with a collection of
values with matching keys.
Console.WriteLine(string.Join(",", numbers1to8));
//1,2,3,4,5,6,7,8
Note that duplicates are removed from the result. If this is undesirable, use Concat instead.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 41
Section 8.41: ToArray
var numbers = new[] {1,2,3,4,5,6,7,8,9,10};
var someNumbers = numbers.Where(n => n < 6);
Console.WriteLine(someNumbers.GetType().Name);
//WhereArrayIterator`1
Console.WriteLine(someNumbersArray.GetType().Name);
//Int32[]
Console.WriteLine(someNumbers.GetType().Name);
//WhereArrayIterator`1
Console.WriteLine(
someNumbersList.GetType().Name + " - " +
someNumbersList.GetType().GetGenericArguments()[0].Name);
//List`1 - Int32
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 42
var oddNumbers = numbers.SkipWhile(n => (n & 1) == 0);
Console.WriteLine(string.Join(",", oddNumbers.ToArray()));
//1,3,5,7
Console.WriteLine(string.Join(",", evenNumbers.ToArray()));
//2,4,6
class Project
{
public int DeveloperId { get; set; }
public string Name { get; set; }
}
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 43
new Project {
DeveloperId = 1,
Name = "Hello World 3D"
},
new Project {
DeveloperId = 1,
Name = "Super Fizzbuzz Maker"
},
new Project {
DeveloperId = 2,
Name = "Citizen Kane - The action game"
},
new Project {
DeveloperId = 2,
Name = "Pro Pong 2016"
}
};
class Pet
{
public string Name { get; set; }
public Person Owner { get; set; }
}
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 44
var query =
from person in people
join pet in pets on person equals pet.Owner into gj
from subpet in gj.DefaultIfEmpty()
select new
{
person.FirstName,
PetName = subpet?.Name ?? "-" // Use - if he has no pet
};
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 45
Chapter 9: ForEach
Section 9.1: Extension method for IEnumerable
ForEach() is defined on the List<T> class, but not on IQueryable<T> or IEnumerable<T>. You have two choices in
those cases:
ToList first
The enumeration (or query) will be evaluated, copying the results into a new list or calling the database. The
method is then called on each item.
This method has obvious memory usage overhead, as an intermediate list is created.
Extension method
Use:
Caution: The Framework's LINQ methods have been designed with the intention of being pure, which means they
do not produce side effects. The ForEach method's only purpose is to produce side effects, and deviates from the
other methods in this aspect. You may consider just using a plain foreach loop instead.
customers.Add(new Customer());
customers.Add(new Customer());
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 46
Chapter 10: Reflection
Section 10.1: What is an Assembly?
Assemblies are the building block of any Common Language Runtime (CLR) application. Every type you define,
together with its methods, properties and their bytecode, is compiled and packaged inside an Assembly.
using System.Reflection;
Assemblies are self-documenting: they do not only contain types, methods and their IL code, but also the Metadata
necessary to inspect and consume them, both at compile and runtime:
Console.WriteLine(typeof(int).Assembly.FullName);
// Will print: "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
If this name includes a PublicKeyToken, it is called a strong name. Strong-naming an assembly is the process of
creating a signature by using the private key that corresponds to the public key distributed with the assembly. This
signature is added to the Assembly manifest, which contains the names and hashes of all the files that make up the
assembly, and its PublicKeyToken becomes part of the name. Assemblies that have the same strong name should
be identical; strong names are used in versioning and to prevent assembly conflicts.
return true;
}
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 47
public override int GetHashCode()
{
var accumulator = 0;
var fields = GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic |
BindingFlags.Public);
foreach (var field in fields)
accumulator = unchecked ((accumulator * 937) ^ field.GetValue(this).GetHashCode());
return accumulator;
}
}
Note: this example do a field based comparasion (ignore static fields and properties) for simplicity
T variable = Activator.CreateInstance(typeof(T));
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 48
if (attributeCache.TryGetValue(value, out temp))
{
return (T) temp;
}
else
{
// Get the type of the struct passed in.
Type type = value.GetType();
FieldInfo fieldInfo = type.GetField(value.ToString());
// Get the custom attributes of the type desired found on the struct.
T[] attribs = (T[])fieldInfo.GetCustomAttributes(typeof(T), false);
return result;
}
}
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 49
Chapter 11: Expression Trees
Section 11.1: building a predicate of form field == value
To build up an expression like _ => _.Field == "VALUE" at runtime.
Given a predicate _ => _.Field and a string value "VALUE", create an expression that tests whether or not the
predicate is true.
This method will build an appropriate Equal expression that tests whether or not Field equals "VALUE".
The predicate can be used by including the predicate in a Where extension method.
Because the C# compiler sees that the lambda expression is assigned to an Expression type rather than a delegate
type it generates an expression tree roughly equivalent to this code
The root of the tree is the lambda expression which contains a body and a list of parameters. The lambda has 1
parameter called "a". The body is a single expression of CLR type BinaryExpression and NodeType of Add. This
expression represents addition. It has two subexpressions denoted as Left and Right. Left is the
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 50
ParameterExpression for the parameter "a" and Right is a ConstantExpression with the value 1.
The expression tree can be compiled into a C# delegate and executed by the CLR
Usually expressions are translated to other languages like SQL, but can be also used to invoke private, protected
and internal members of public or non-public types as alternative to Reflection.
public TestClass
{
public static string StaticPublicField = "StaticPublicFieldValue";
}
It can be then i.e. compiled into a delegate for retrieving field value.
Problem We want to get on the items which have "car" in their description. We need to check it for null before
searching for a string inside but we don't want it to be called excessively, as the computation could be expensive.
using System;
using System.Linq;
using System.Linq.Expressions;
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 51
new Element { Description = "car" },
new Element { Description = "cargo" },
new Element { Description = "wheel" },
new Element { Description = null },
new Element { Description = "Madagascar" },
};
Console.WriteLine(elementIsInterestingExpression.ToString());
var countExpensiveComputations = 0;
Action incCount = () => countExpensiveComputations++;
elements
.Where(
CreateSearchPredicate(
"car",
(Element e) => ExpensivelyComputed(
e, incCount
)
).Compile()
)
.Count();
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 52
var elementParameter = Expression.Parameter(typeof(T), "element");
Output
First thing to note is how the actual propery access, wrapped in an Invoke:
, and this is the only part that touches e.Description, and in place of it, extracted parameter of type string is
passed to the next one:
Another important thing to note here is AndAlso. It computes only the left part, if the first part returns 'false'. It's a
common mistake to use the bitwise operator 'And' instead of it, which always computes both parts, and would fail
with a NullReferenceException in this example.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 53
Chapter 12: Custom Types
Section 12.1: Struct Definition
Structs inherit from System.ValueType, are value types, and live on the stack. When value types are passed
as a parameter, they are passed by value.
Struct MyStruct
{
public int x;
public int y;
}
Passed by value means that the value of the parameter is copied for the method, and any changes made to the
parameter in the method are not reflected outside of the method. For instance, consider the following code, which
calls a method named AddNumbers, passing in the variables a and b, which are of type int, which is a Value type.
int a = 5;
int b = 6;
AddNumbers(a,b);
Even though we added 5 to x inside the method, the value of a remains unchanged, because it's a Value type, and
that means x was a copy of a's value, but not actually a.
Remember, Value types live on the stack, and are passed by value.
Passed by reference means that a reference to the parameter is passed to the method, and any changes to the
parameter will be reflected outside of the method when it returns, because the reference is to the exact same object
in memory. Let's use the same example as before, but we'll "wrap" the ints in a class first.
AddNumbers(instanceOfMyClass);
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 54
int z = sample.a + sample.b; // z becomes 11
sample.a = sample.a + 5; // now we changed a to be 10
z = sample.a + sample.b; // now z becomes 16
}
This time, when we changed sample.a to 10, the value of instanceOfMyClass.a also changes, because it was passed
by reference. Passed by reference means that a reference (also sometimes called a pointer) to the object was passed
into the method, instead of a copy of the object itself.
Remember, Reference types live on the heap, and are passed by reference.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 55
Chapter 13: Code Contracts
Section 13.1: Contracts for Interfaces
Using Code Contracts it is possible to apply a contract to an interface. This is done by declaring an abstract class
that implments the interfaces. The interface should be tagged with the ContractClassAttribute and the contract
definition (the abstract class) should be tagged with the ContractClassForAttribute
C# Example...
[ContractClass(typeof(MyInterfaceContract))]
public interface IMyInterface
{
string DoWork(string input);
}
//Never inherit from this contract defintion class
[ContractClassFor(typeof(IMyInterface))]
internal abstract class MyInterfaceContract : IMyInterface
{
private MyInterfaceContract() { }
Under Extensions and Updates search for Code Contracts then install the Code Contracts Tools
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 56
After the tools are installed you must enable Code Contracts within your Project solution. At the minimum you
probably want to enable the Static Checking (check after build). If you are implementing a library that will be used
by other solutions you may want to consider also enabling Runtime Checking.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 57
Section 13.3: Preconditions
Preconditions allows methods to provide minimum required values for input parameters
Example...
//do work
}
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 58
Static Analysis Result...
Example...
string GetValue()
{
Contract.Ensures(Contract.Result<string>() != null);
return null;
}
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 59
Chapter 14: Settings
Section 14.1: AppSettings from ConfigurationSettings in .NET
1.x
Deprecated usage
The ConfigurationSettings class was the original way to retrieve settings for an assembly in .NET 1.0 and 1.1. It has
been superseded by the ConfigurationManager class and the WebConfigurationManager class.
If you have two keys with the same name in the appSettings section of the configuration file, the last one is used.
app.config
Program.cs
using System;
using System.Configuration;
using System.Diagnostics;
namespace ConsoleApplication1
{
class Program
{
static void Main()
{
string keyValue = ConfigurationSettings.AppSettings["keyName"];
Debug.Assert("anything, as a string".Equals(keyValue));
Console.ReadKey();
}
}
}
app.config
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 60
<appSettings>
<add key="keyName" value="anything, as a string"/>
<add key="keyNames" value="123"/>
<add key="keyNames" value="234"/>
</appSettings>
</configuration>
Program.cs
using System;
using System.Configuration;
using System.Diagnostics;
namespace ConsoleApplication1
{
class Program
{
static void Main()
{
string keyValue = ConfigurationManager.AppSettings["keyName"];
Debug.Assert("anything, as a string".Equals(keyValue));
Console.ReadKey();
}
}
}
1. Settings can be made strongly typed. Any type which can be serialized can be used for a settings value.
2. Application settings can be easily separated from user settings. Application settings are stored in a single
configuration file: web.config for Web sites and Web applications, and app.config, renamed as
assembly.exe.config, where assembly is the name of the executable. User settings (not used by Web projects)
are stored in a user.config file in the user's Application Data folder (which varies with the operating system
version).
3. Application settings from class libraries can be combined into a single configuration file without risk of name
collisions, since each class library can have its own custom settings section.
In most project types, the Project Properties Designer has a Settings tab which is the starting point for creating
custom application and user settings. Initially, the Settings tab will be blank, with a single link to create a default
settings file. Clicking the link results in these changes:
1. If a configuration file (app.config or web.config) does not exist for the project, one will be created.
2. The Settings tab will be replaced with a grid control which enables you to create, edit, and delete individual
settings entries.
3. In Solution Explorer, a Settings.settings item is added under the Properties special folder. Opening this
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 61
item will open the Settings tab.
4. A new file with a new partial class is added under the Properties folder in the project folder. This new file is
named Settings.Designer.__ (.cs, .vb, etc.), and the class is named Settings. The class is code-generated,
so it should not be edited, but the class is a partial class, so you can extend the class by putting additional
members in a separate file. Furthermore, the class is implemented using the Singleton Pattern, exposing the
singleton instance with the property named Default.
As you add each new entry to the Settings tab, Visual Studio does these two things:
1. Saves the setting in the configuration file, in a custom configuration section designed to be managed by the
Settings class.
2. Creates a new member in the Settings class to read, write, and present the setting in the specific type
selected from the Settings tab.
Add an application setting named ExampleTimeout, using the time System.Timespan, and set the value to 1 minute:
Save the Project Properties, which saves the Settings tab entries, as well as re-generates the custom Settings class
and updates the project configuration file.
Program.cs
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 62
using System;
using System.Diagnostics;
using ConsoleApplication1.Properties;
namespace ConsoleApplication1
{
class Program
{
static void Main()
{
TimeSpan exampleTimeout = Settings.Default.ExampleTimeout;
Debug.Assert(TimeSpan.FromMinutes(1).Equals(exampleTimeout));
Console.ReadKey();
}
}
}
Look in the project configuration file to see how the application setting entry has been created:
Notice that the appSettings section is not used. The applicationSettings section contains a custom namespace-
qualified section that has a setting element for each entry. The type of the value is not stored in the configuration
file; it is only known by the Settings class.
Look in the Settings class to see how it uses the ConfigurationManager class to read this custom section.
...
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("00:01:00")]
public global::System.TimeSpan ExampleTimeout {
get {
return ((global::System.TimeSpan)(this["ExampleTimeout"]));
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 63
}
}
...
Notice that a DefaultSettingValueAttribute was created to stored the value entered in the Settings tab of the
Project Properties Designer. If the entry is missing from the configuration file, this default value is used instead.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 64
Chapter 15: Regular Expressions
(System.Text.RegularExpressions)
Section 15.1: Check if pattern matches input
public bool Check()
{
string input = "Hello World!";
string pattern = @"H.ll. W.rld!";
// true
return Regex.IsMatch(input, pattern);
}
// true
return Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase | RegexOptions.Singleline);
}
// World
return match.Groups["Subject"].Value;
}
Code
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 65
static void Main(string[] args)
{
string input = "Carrot Banana Apple Cherry Clementine Grape";
// Find words that start with uppercase 'C'
string pattern = @"\bC\w*\b";
Output
Carrot
Cherry
Clementine
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 66
Chapter 16: File Input/Output
Parameter Details
string path Path of the file to check. (relative or fully qualified)
if(File.Exists(filePath))
{
Console.WriteLine("Exists");
}
else
{
Console.WriteLine("Does not exist");
}
}
}
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 67
{
writer.WriteLine("Text to Write\n");
}
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 68
Chapter 17: System.IO
Section 17.1: Reading a text file using StreamReader
string fullOrRelativePath = "testfile.txt";
string fileData;
Note that this StreamReader constructor overload does some auto encoding detection, which may or may not
conform to the actual encoding used in the file.
Please note that there are some convenience methods that read all text from file available on the System.IO.File
class, namely File.ReadAllText(path) and File.ReadAllLines(path).
NOTE: Those are just three of the seven overloads of the constructor for the SerialPort type.
The simplest way is to use the SerialPort.Read and SerialPort.Write methods. However you can also retrieve a
System.IO.Stream object which you can use to stream data over the SerialPort. To do this, use
SerialPort.BaseStream.
Reading
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 69
Or simply read to the first newline encountered in the incoming data:
Writing
However you can also send data over like this when needed:
//Note that you can swap out the byte-array with a char-array if you so choose.
byte[] data = new byte[1] { 255 };
port.Write(data, 0, data.Length);
On the first line, we read all the data in the file as a string.
On the second line, we read the data in the file into a string-array. Each line in the file becomes an element in
the array.
On the third we read the bytes from the file.
Next, let's see three different methods of appending data to a file. If the file you specify doesn't exist, each method
will automatically create the file before attempting to append the data to it.
On the first line we simply add a string to the end of the specified file.
On the second line we add each element of the array onto a new line in the file.
Finally on the third line we use File.AppendText to open up a streamwriter which will append whatever data
is written to it.
And lastly, let's see three different methods of writing data to a file. The difference between appending and writing
being that writing over-writes the data in the file while appending adds to the data in the file. If the file you specify
doesn't exist, each method will automatically create the file before attempting to write the data to it.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 70
And the third line allows you to write a byte array to the file.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 71
Chapter 18: System.IO.File class
Parameter Details
source The file that is to be moved to another location.
The directory in which you would like to move source to (this variable should also contain the name
destination
(and file extension) of the file.
File.Delete(path);
Note that last point (file does not exist) is usually circumvented with a code snippet like this:
if (File.Exists(path))
File.Delete(path);
However it's not an atomic operation and file may be delete by someone else between the call to File.Exists()
and before File.Delete(). Right approach to handle I/O operation requires exception handling (assuming an
alternative course of actions may be taken when operation fails):
if (File.Exists(path))
{
try
{
File.Delete(path);
}
catch (IOException exception)
{
if (!File.Exists(path))
return; // Someone else deleted this file
Note that this I/O errors sometimes are transitory (file in use, for example) and if a network connection is involved
then it may automatically recover without any action from our side. It's then common to retry an I/O operation few
times with a small delay between each attempt:
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 72
return;
if (i == NumberOfAttempts)
throw;
Thread.Sleep(DelayBetweenEachAttempt);
}
Note: in Windows environment file will not be really deleted when you call this function, if someone else open the
file using FileShare.Delete then file can be deleted but it will effectively happen only when owner will close the
file.
In this example we read all lines from a file and drop all blank lines then we write back to original path:
File.WriteAllLines(path,
File.ReadAllLines(path).Where(x => !String.IsNullOrWhiteSpace(x)));
If file is too big to load it in memory and output path is different from input path:
File.WriteAllLines(outputPath,
File.ReadLines(inputPath).Where(x => !String.IsNullOrWhiteSpace(x)));
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 73
When performing conversions do not forget that file may contain BOM (Byte Order Mark), to better understand
how it's managed refer to Encoding.UTF8.GetString doesn't take into account the Preamble/BOM.
File.Move(@"C:\TemporaryFile.txt", @"C:\TemporaryFiles\TemporaryFile.txt");
However, there are many things that could go wrong with this simple operation. For instance, what if the user
running your program does not have a Drive that is labelled 'C'? What if they did - but they decided to rename it to
'B', or 'M'?
What if the Source file (the file in which you would like to move) has been moved without your knowing - or what if
it simply doesn't exist.
This can be circumvented by first checking to see whether the source file does exist:
This will ensure that at that very moment, the file does exist, and can be moved to another location. There may be
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 74
times where a simple call to File.Exists won't be enough. If it isn't, check again, convey to the user that the
operation failed - or handle the exception.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 75
Chapter 19: Reading and writing Zip files
The ZipFile class lives in the System.IO.Compression namespace. It can be used to read from, and write to Zip
files.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 76
using (ZipArchive archive = new ZipArchive(fs, ZipArchiveMode.Update))
{
// Add file to root
archive.CreateEntryFromFile("test.txt", "test.txt");
There is also the option to write directly to a file within the archive:
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 77
Chapter 20: Managed Extensibility
Framework
Section 20.1: Connecting (Basic)
See the other (Basic) examples above.
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
namespace Demo
{
public static class Program
{
public static void Main()
{
using (var catalog = new ApplicationCatalog())
using (var exportProvider = new CatalogExportProvider(catalog))
using (var container = new CompositionContainer(exportProvider))
{
exportProvider.SourceProvider = container;
Other types of catalogs (e.g., DirectoryCatalog) can be used instead of (or in addition to) ApplicationCatalog, to
look in other places for exports that satisfy the imports.
namespace Demo
{
[Export(typeof(IUserProvider))]
public sealed class UserProvider : IUserProvider
{
public ReadOnlyCollection<User> GetAllUsers()
{
return new List<User>
{
new User(0, "admin"),
new User(1, "Dennis"),
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 78
new User(2, "Samantha"),
}.AsReadOnly();
}
}
}
This could be defined virtually anywhere; all that matters is that the application knows where to look for it (via the
ComposablePartCatalogs it creates).
namespace Demo
{
public sealed class UserWriter
{
[Import(typeof(IUserProvider))]
private IUserProvider userProvider;
This is a type that has a dependency on an IUserProvider, which could be defined anywhere. Like the previous
example, all that matters is that the application knows where to look for the matching export (via the
ComposablePartCatalogs it creates).
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 79
Chapter 21: SpeechRecognitionEngine class
to recognize speech
LoadGrammar: Parameters Details
The grammar to load. For example, a DictationGrammar object to allow free
grammar
text dictation.
RecognizeAsync: Parameters Details
The RecognizeMode for the current recognition: Single for just one
mode
recognition, Multiple to allow multiple.
GrammarBuilder.Append: Parameters Details
Appends some choices to the grammar builder. This means that, when the
choices user inputs speech, the recognizer can follow different "branches" from a
grammar.
Choices constructor: Parameters Details
choices An array of choices for the grammar builder. See GrammarBuilder.Append.
Grammar constructor: Parameter Details
builder The GrammarBuilder to construct a Grammar from.
// ...
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 80
Chapter 22:
System.Runtime.Caching.MemoryCache
(ObjectCache)
Section 22.1: Adding Item to Cache (Set)
Set function inserts a cache entry into the cache by using a CacheItem instance to supply the key and value for the
cache entry.
//returns existing item form cache or add the new value if it does not exist.
var cachedItem = _cacheContainer.AddOrGetExisting(key, newValue, policy) as
Lazy<TValue>;
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 81
return default(TValue);
}
}
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 82
Chapter 23: System.Reflection.Emit
namespace
Section 23.1: Creating an assembly dynamically
using System;
using System.Reflection;
using System.Reflection.Emit;
class DemoAssemblyBuilder
{
public static void Main()
{
// An assembly consists of one or more modules, each of which
// contains zero or more types. This code creates a single-module
// assembly, the most common case. The module contains one type,
// named "MyDynamicType", that has a private field, a property
// that gets and sets the private field, constructors that
// initialize the private field, and a method that multiplies
// a user-supplied number by the private field value and returns
// the result. In C# the type might look like this:
/*
public class MyDynamicType
{
private int m_number;
TypeBuilder tb = mb.DefineType(
"MyDynamicType",
TypeAttributes.Public);
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 83
// Add a private field of type int (Int32).
FieldBuilder fbNumber = tb.DefineField(
"m_number",
typeof(int),
FieldAttributes.Private);
ILGenerator il = mbMyMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // Load this - always the first argument of any instance method
il.Emit(OpCodes.Ldfld, fbNumber);
il.Emit(OpCodes.Ldarg_1); // Load the integer argument
il.Emit(OpCodes.Mul); // Multiply the two numbers with no overflow checking
il.Emit(OpCodes.Ret); // Return
// Next, we build the property. This involves building the property itself, as well as the
// getter and setter methods.
PropertyBuilder pbNumber = tb.DefineProperty(
"Number", // Name
PropertyAttributes.None,
typeof(int), // Type of the property
new Type[0]); // Types of indices, if any
il = mbGetNumber.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // Load this
il.Emit(OpCodes.Ldfld, fbNumber); // Load the value of this.m_number
il.Emit(OpCodes.Ret); // Return the value
pbNumber.SetGetMethod(mbGetNumber);
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 84
the same class
ConstructorBuilder intConstructor = tb.DefineConstructor(
MethodAttributes.Public, CallingConventions.Standard | CallingConventions.HasThis,
new[] { typeof(int) });
il = intConstructor.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // this
il.Emit(OpCodes.Call, typeof(object).GetConstructor(new Type[0])); // call parent's
constructor
il.Emit(OpCodes.Ldarg_0); // this
il.Emit(OpCodes.Ldarg_1); // our int argument
il.Emit(OpCodes.Stfld, fbNumber); // store argument in this.m_number
il.Emit(OpCodes.Ret);
// The types from the assembly can be used directly using reflection, or we can save the
assembly to use as a reference
object ourInstance = Activator.CreateInstance(ourType);
Console.WriteLine(ourType.GetProperty("Number").GetValue(ourInstance)); // 42
// Save the assembly for use elsewhere. This is very useful for debugging - you can use e.g.
ILSpy to look at the equivalent IL/C# code.
ab.Save(@"DynamicAssemblyExample.dll");
// Using newly created type
var myDynamicType = tb.CreateType();
var myDynamicTypeInstance = Activator.CreateInstance(myDynamicType);
Console.WriteLine(myDynamicTypeInstance.GetType()); // MyDynamicType
Console.WriteLine(numberField.GetValue(myDynamicTypeInstance)); // 10
}
}
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 85
Chapter 24: .NET Core
.NET Core is a general purpose development platform maintained by Microsoft and the .NET community on GitHub.
It is cross-platform, supporting Windows, macOS and Linux, and can be used in device, cloud, and embedded/IoT
scenarios.
When you think of .NET Core the following should come to mind (flexible deployment, cross-platform, command-
line tools, open source).
Another great thing is that even if it's open source Microsoft is actively supporting it.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 86
Chapter 25: ADO.NET
ADO(ActiveX Data Objects).Net is a tool provided by Microsoft which provides access to data sources such as SQL
Server, Oracle, and XML through its components. .Net front-end applications can retrieve, create, and manipulate
data, once they are connected to a data source through ADO.Net with appropriate privileges.
ADO.Net provides a connection-less architecture. It is a secure approach to interact with a database, since, the
connection doesn't have to be maintained during the entire session.
// best practice - always use the correct types when specifying your parameters, Value
is assigned to a DateTime instance and not a string representation of a Date
sc.Parameters.Add(new SqlParameter("@dateOfBirth", SqlDbType.Date){ Value =
newEmployee.DateOfBirth });
// best practice - open your connection as late as possible unless you need to verify
that the database connection is valid and won't fail and the proceeding code execution takes a long
time (not the case here)
con.Open();
sc.ExecuteNonQuery();
}
// the end of the using block will close and dispose the SqlConnection
// best practice - end the using block as soon as possible to release the database connection
}
}
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 87
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DateOfBirth { get; set; }
}
Rule of thumb is to open connection for minimal time. Close the connection explicitly once your procedure
execution is over this will return the connection object back to connection pool. Default connection pool max
size = 100. As connection pooling enhances the performance of physical connection to SQL
Server.Connection Pooling in SQL Server
Wrap all database connections in a using block so they are always closed & disposed even in the event of an
Exception. See using Statement (C# Reference) for more information on using statements
Retrieve the connection strings by name from the app.config or web.config (depending on the application
type)
This requires an assembly reference to System.configuration
See Connection Strings and Configuration Files for additional information on how to structure your
configuration file
Always use parameters for incoming values to
Avoid sql injection attacks
Avoid errors if malformed text is used like including a single quote which is the sql equivalent of
escaping or starting a string (varchar/nvarchar)
Letting the database provider reuse query plans (not supported by all database providers) which
increases efficiency
When working with parameters
Sql parameters type and size mismatch is a common cause of insert/ updated/ select failure
Give your Sql parameters meaningful names just like you do variables in your code
Specify the database data type of the column you are using, this ensures the wrong parameter types is
not used which could lead to unexpected results
Validate your incoming parameters before you pass them into the command (as the saying goes,
"garbage in, garbage out"). Validate incoming values as early as possible in the stack
Use the correct types when assigning your parameter values, example: do not assign the string value
of a DateTime, instead assign an actual DateTime instance to the value of the parameter
Specify the size of string-type parameters. This is because SQL Server can re-use execution plans if the
parameters match in type and size. Use -1 for MAX
Do not use the method AddWithValue, the main reason is it is very easy to forget to specify the
parameter type or the precision/scale when needed. For additional information see Can we stop using
AddWithValue already?
When using database connections
Open the connection as late as possible and close it as soon as possible. This is a general guideline
when working with any external resource
Never share database connection instances (example: having a singleton host a shared instance of
type SqlConnection). Have your code always create a new database connection instance when needed
and then have the calling code dispose of it and "throw it away" when it is done. The reason for this is
Most database providers have some sort of connection pooling so creating new managed
connections is cheap
It eliminates any future errors if the code starts working with multiple threads
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 88
// User Id=...;Password=...; to use SQL Server authentication instead. You may
// want to find the appropriate connection string for your server.
string connectionString =
@"Server=myServer\myInstance;Database=myDataBase;Trusted_Connection=True;"
// Most ADO.NET objects are disposable and, thus, require the using keyword.
using (var connection = new SqlConnection(connectionString))
using (var command = new SqlCommand(sql, connection))
{
// Use parameters instead of string concatenation to add user-supplied
// values to avoid SQL injection and formatting issues. Explicitly supply datatype.
connection.Open();
command.ExecuteNonQuery();
}
Note 1: Please see SqlDbType Enumeration for the MSFT SQL Server-specific variation.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 89
Chapter 26: Dependency Injection
Section 26.1: How Dependency Injection Makes Unit Testing
Easier
This builds on the previous example of the Greeter class which has two dependencies, IGreetingProvider and
IGreetingWriter.
The actual implementation of IGreetingProvider might retrieve a string from an API call or a database. The
implementation of IGreetingWriter might display the greeting in the console. But because Greeter has its
dependencies injected into its constructor, it's easy to write a unit test that injects mocked versions of those
interfaces. In real life we might use a framework like Moq, but in this case I'll write those mocked implementations.
[TestClass]
public class GreeterTests
{
[TestMethod]
public void Greeter_WritesGreeting()
{
var greetingProvider = new TestGreetingProvider();
var greetingWriter = new TestGreetingWriter();
var greeter = new Greeter(greetingProvider, greetingWriter);
greeter.Greet();
Assert.AreEqual(greetingWriter[0], TestGreetingProvider.TestGreeting);
}
}
The behavior of IGreetingProvider and IGreetingWriter are not relevant to this test. We want to test that
Greeter gets a greeting and writes it. The design of Greeter (using dependency injection) allows us to inject mocked
dependencies without any complicated moving parts. All we're testing is that Greeter interacts with those
dependencies as we expect it to.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 90
public class Greeter
{
private readonly IGreetingProvider _greetingProvider;
private readonly IGreetingWriter _greetingWriter;
The Greeting class depends on both IGreetingProvider and IGreetingWriter, but it is not responsible for
creating instances of either. Instead it requires them in its constructor. Whatever creates an instance of Greeting
must provide those two dependencies. We can call that "injecting" the dependencies.
Because dependencies are provided to the class in its constructor, this is also called "constructor injection."
The constructor saves the dependencies as private fields. As soon as the class is instantiated, those
dependencies are available to all other non-static methods of the class.
The private fields are readonly. Once they are set in the constructor they cannot be changed. This indicates
that those fields should not (and cannot) be modified outside of the constructor. That further ensures that
those dependencies will be available for the lifetime of the class.
The dependencies are interfaces. This is not strictly necessary, but is common because it makes it easier to
substitute one implementation of the dependency with another. It also allows providing a mocked version of
the interface for unit testing purposes.
This is not the same thing as using a dependency injection framework (often called a "DI container", "IoC container",
or just "container") like Castle Windsor, Autofac, SimpleInjector, Ninject, Unity, or others.
A container just makes dependency injection easier. For example, suppose you write a number of classes that rely
on dependency injection. One class depends on several interfaces, the classes that implement those interfaces
depend on other interfaces, and so on. Some depend on specific values. And just for fun, some of those classes
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 91
implement IDisposable and need to be disposed.
Each individual class is well-written and easy to test. But now there's a different problem: Creating an instance of a
class has become much more complicated. Suppose we're creating an instance of a CustomerService class. It has
dependencies and its dependencies have dependencies. Constructing an instance might look something like this:
You might wonder, why not put the whole giant construction in a separate function that just returns
CustomerService? One reason is that because the dependencies for each class are injected into it, a class isn't
responsible for knowing whether those dependencies are IDisposable or disposing them. It just uses them. So if a
we had a GetCustomerService() function that returned a fully-constructed CustomerService, that class might
contain a number of disposable resources and no way to access or dispose them.
And aside from disposing IDisposable, who wants to call a series of nested constructors like that, ever? That's a
short example. It could get much, much worse. Again, that doesn't mean that we wrote the classes the wrong way.
The classes might be individually perfect. The challenge is composing them together.
A dependency injection container simplifies that. It allows us to specify which class or value should be used to fulfill
each dependency. This slightly oversimplified example uses Castle Windsor:
We call this "registering dependencies" or "configuring the container." Translated, this tells our WindsorContainer:
If a class requires ILogWriter, create an instance of LogWriter. LogWriter requires a file path. Use this value
from AppSettings.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 92
If a class requires IAuthorizationRepository, create an instance of SqlAuthorizationRepository. It
requires a connection string. Use this value from the ConnectionStrings section.
If a class requires ICustomerDataProvider, create a CustomerApiClient and provide the string it needs from
AppSettings.
When we request a dependency from the container we call that "resolving" a dependency. It's bad practice to do
that directly using the container, but that's a different story. For demonstration purposes, we could now do this:
If it gets to a point where a class requires a dependency that we haven't registered, like IDoesSomethingElse, then
when we try to resolve CustomerService it will throw a clear exception telling us that we haven't registered
anything to fulfill that requirement.
Each DI framework behaves a little differently, but typically they give us some control over how certain classes are
instantiated. For example, do we want it to create one instance of LogWriter and provide it to every class that
depends on ILogWriter, or do we want it to create a new one every time? Most containers have a way to specify
that.
What about classes that implement IDisposable? That's why we call container.Release(customerService); at the
end. Most containers (including Windsor) will step back through all of the dependencies created and Dispose the
ones that need disposing. If CustomerService is IDisposable it will dispose that too.
Registering dependencies as seen above might just look like more code to write. But when we have lots of classes
with lots of dependencies then it really pays off. And if we had to write those same classes without using
dependency injection then that same application with lots of classes would become difficult to maintain and test.
This scratches the surface of why we use dependency injection containers. How we configure our application to use
one (and use it correctly) is not just one topic - it's a number of topics, as the instructions and examples vary from
one container to the next.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 93
Chapter 27: Platform Invoke
Section 27.1: Marshaling structs
Simple struct
C++ signature:
C# definition
C++ signature
typedef struct
{
int length;
int *data;
} VECTOR;
The data array should be defined as IntPtr and memory should be explicitly allocated with
Marshal.AllocHGlobal() (and freed with Marshal.FreeHGlobal() afterwords):
[StructLayout(LayoutKind.Sequential)]
public struct VECTOR : IDisposable
{
int length;
IntPtr dataBuf;
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 94
if (value != null && value.Length > 0)
{
dataBuf = Marshal.AllocHGlobal(value.Length * Marshal.SizeOf(value[0]));
Marshal.Copy(value, 0, dataBuf, value.Length);
length = value.Length;
}
}
}
void FreeDataBuf()
{
if (dataBuf != IntPtr.Zero)
{
Marshal.FreeHGlobal(dataBuf);
dataBuf = IntPtr.Zero;
}
}
public void Dispose()
{
FreeDataBuf();
}
}
[DllImport("vectors.dll")]
public static extern void SetVector([In]ref VECTOR vector);
C++ signature:
typedef struct
{
char *name;
} USER;
When such data is passed out of unmanaged code and memory is allocated by the unmanaged functions, the
managed caller should receive it into an IntPrt variable and convert the buffer to a managed array. In case of
strings there is a convenient Marshal.PtrToStringAnsi() method:
[StructLayout(LayoutKind.Sequential)]
public struct USER
{
IntPtr nameBuffer;
public string name { get { return Marshal.PtrToStringAnsi(nameBuffer); } }
}
[DllImport("users.dll")]
public static extern bool GetCurrentUser(out USER user);
C++ declaration
typedef union
{
char c;
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 95
int i;
} CharOrInt;
C# declaration
[StructLayout(LayoutKind.Explicit)]
public struct CharOrInt
{
[FieldOffset(0)]
public byte c;
[FieldOffset(0)]
public int i;
}
Overlapping a reference value with a value type one is not allowed so you cannot simply use the FieldOffset(0)
text; FieldOffset(0) i; will not compile for
typedef union
{
char text[128];
int i;
} TextOrInt;
and generally you would have to employ custom marshaling. However, in particular cases like this simpler technics
may be used:
[StructLayout(LayoutKind.Sequential)]
public struct TextOrInt
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
public byte[] text;
public int i { get { return BitConverter.ToInt32(text, 0); } }
}
class PInvokeExample
{
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern uint MessageBox(IntPtr hWnd, String text, String caption, int options);
Declare a function as static extern stting DllImportAttribute with its Value property set to .dll name. Don't
forget to use System.Runtime.InteropServices namespace. Then call it as an regular static method.
The Platform Invocation Services will take care of loading the .dll and finding the desired finction. The P/Invoke in
most simple cases will also marshal parameters and return value to and from the .dll (i.e. convert from .NET
datatypes to Win32 ones and vice versa).
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 96
Section 27.4: Using Windows API
Use pinvoke.net.
Before declaring an extern Windows API function in your code, consider looking for it on pinvoke.net. They most
likely already have a suitable declaration with all supporting types and good examples.
[DllImport("Example.dll")]
static extern void SetArray(
[MarshalAs(UnmanagedType.LPArray, SizeConst = 128)]
byte[] data);
Arrays of string
[DllImport("Example.dll")]
static extern void SetStrArray(string[] textLines);
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 97
Chapter 28: NuGet packaging system
Section 28.1: Uninstalling a package from one project in a
solution
PM> Uninstall-Package -ProjectName MyProjectB EntityFramework
Starting with Visual Studio 2012, NuGet is included in every edition, and can be used from: Tools -> NuGet Package
Manager -> Package Manager Console.
You do so through the Tools menu of Visual Studio, clicking Extensions and Updates:
Available through clicking "Manage NuGet Packages..." on a project or its References folder
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 98
Tools -> NuGet Package Manager -> Package Manager Console.
Here you can issue, amongst others, install-package commands which installs the entered package into the
currently selected "Default project":
Install-Package Elmah
You can also provide the project to install the package to, overriding the selected project in the "Default project"
dropdown:
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 99
where EntityFramework is the name of the package to be updated. Note that update will run for all projects, and so
is different from Install-Package EntityFramework which would install to "Default project" only.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 100
Chapter 29: Globalization in ASP.NET MVC
using Smart internationalization for
ASP.NET
Section 29.1: Basic configuration and setup
1. Add the I18N nuget package to your MVC project.
2. In web.config, add the i18n.LocalizingModule to your <httpModules> or <modules> section.
3. Add a folder named "locale" to the root of your site. Create a subfolder for each culture you wish to support.
For example, /locale/fr/.
4. In each culture-specific folder, create a text file named messages.po.
5. For testing purposes, enter the following lines of text in your messages.po file:
#: Translation test
msgid "Hello, world!"
msgstr "Bonjour le monde!"
using System.Web.Mvc;
namespace I18nDemo.Controllers
{
public class DefaultController : Controller
{
public ActionResult Index()
{
// Text inside [[[triple brackets]]] must precisely match
// the msgid in your .po file.
return Content("[[[Hello, world!]]]");
}
}
}
7. Run your MVC application and browse to the route corresponding to your controller action, such as
http://localhost:[yourportnumber]/default.
Observe that the URL is changed to reflect your default culture, such as
http://localhost:[yourportnumber]/en/default.
8. Replace /en/ in the URL with /fr/ (or whatever culture you've selected.) The page should now display the
translated version of your text.
9. Change your browser's language setting to prefer your alternate culture and browse to /default again.
Observe that the URL is changed to reflect your alternate culture and the translated text appears.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 101
10. In web.config, add handlers so that users cannot browse to your locale folder.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 102
Chapter 30: System.Net.Mail
Section 30.1: MailMessage
Here is the example of creating of mail message with attachments. After creating we send this message with the
help of SmtpClient class. Default 25 port is used here.
MyMail.Subject = subject;
MyMail.IsBodyHtml = true;
MyMail.Body = body;
MyMail.Priority = MailPriority.Normal;
smtpMailObj.Send(MyMail);
return true;
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 103
}
}
catch
{
return false;
}
}
}
using System.Net.Mail;
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 104
Chapter 31: Using Progress<T> and
IProgress<T>
Section 31.1: Simple Progress reporting
IProgress<T> can be used to report progress of some procedure to another procedure. This example shows how
you can create a basic method that reports its progress.
void Main()
{
IProgress<int> p = new Progress<int>(progress =>
{
Console.WriteLine("Running Step: {0}", progress);
});
LongJob(p);
}
Output:
Running Step: 0
Running Step: 3
Running Step: 4
Running Step: 5
Running Step: 6
Running Step: 7
Running Step: 8
Running Step: 9
Running Step: 2
Running Step: 1
Note that when you this code runs, you may see numbers be output out of order. This is because the
IProgress<T>.Report() method is run asynchronously, and is therefore not as suitable for situations where the
progress must be reported in order.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 105
var p3 = new Progress<int>();
((IProgress<int>)p3).Report(3); //works
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 106
Chapter 32: JSON Serialization
Section 32.1: Deserialization using
System.Web.Script.Serialization.JavaScriptSerializer
The JavaScriptSerializer.Deserialize<T>(input) method attempts to deserialize a string of valid JSON into an
object of the specified type <T>, using the default mappings natively supported by JavaScriptSerializer.
using System.Collections;
using System.Web.Script.Serialization;
// ...
Person person = new Person { PersonName = "Andrius", PersonAge = 99, Address = "Some address" };
string rawJson = JsonConvert.SerializeObject(person);
Console.WriteLine(rawJson); // {"name":"Andrius","age":99}
Notice how properties (and classes) can be decorated with attributes to change their appearance in resulting json
string or to remove them from json string at all (JsonIgnore).
In C#, public identifiers are written in PascalCase by convention. In JSON, the convention is to use camelCase for all
names. You can use a contract resolver to convert between the two.
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 107
public string Address { get; set; }
}
Console.WriteLine(json); // {"name":"Andrius","age":99}
}
using Newtonsoft.Json;
// ...
Serialization
De-serialization
Notice that the keys in the rawJson object have been turned into member variables in the dynamic object.
This is useful in cases where an application can accept/ produce varying formats of JSON. It is however suggested to
use an extra level of validation for the Json string or to the dynamic object generated as a result of serialization/ de-
serialization.
Another serious serializer issue in .net is the self referencing loop. In the case of a student that is enrolled in a
course, its instance has a course property and a course has a collection of students that means a List<Student>
which will create a reference loop. You can handle this with JsonSerializerSettings :
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 109
Chapter 33: JSON in .NET with
Newtonsoft.Json
The NuGet package Newtonsoft.Json has become the defacto standard for using and manipulating JSON formatted
text and objects in .NET. It is a robust tool that is fast, and easy to use.
This yields a Person object with Name "Joe Smith" and Age 21.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 110
Chapter 34: XmlSerializer
Section 34.1: Formatting: Custom DateTime format
public class Dog
{
private const string _birthStringFormat = "yyyy-MM-dd";
[XmlIgnore]
public DateTime Birth {get; set;}
[XmlElement(ElementName="Birth")]
public string BirthString
{
get { return Birth.ToString(_birthStringFormat); }
set { Birth = DateTime.ParseExact(value, _birthStringFormat, CultureInfo.InvariantCulture);
}
}
}
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 111
public List<Product> Products {get; set; }
}
Sometimes we can't provide all of the required metadata needed for the XmlSerializer framework in attribute.
Suppose we have a base class of serialized objects, and some of the derived classes are unknown to the base class.
We can't place an attribute for all of the classes which are not know at the design time of the base type. We could
have another team developing some of the derived classes.
What can we do
We can use new XmlSerializer(type, knownTypes), but that would be a O(N^2) operation for N serializers, at least
to discover all of the types supplied in arguments:
In this example, the the Base type is not aware of it's derived types, which is normal in OOP.
Doing it efficiently
Luckily, there is a method which addresses this particular problem - supplying known types for multiple serializers
efficiently:
The FromTypes method allows you to efficiently create an array of XmlSerializer objects for processing an
array of Type objects.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 112
using System;
using System.Collections.Generic;
using System.Xml.Serialization;
using System.Linq;
using System.Linq;
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 113
}
Output:
0
0
This error message recommends what we tried to avoid (or what we can not do in some scenarios) - referencing
derived types from base class:
Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.
<Base xsi:type="Derived">
Base corresponds to the property type declared in the Container type, and Derived being the type of the instance
actually supplied.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 114
Chapter 35: VB Forms
Section 35.1: Hello World in VB.NET Forms
To show a message box when the form has been shown:
Load() will be called first, and only once, when the form first loads. Show() will be called every time the user
launches the form. Activate() will be called every time the user makes the form active.
Load() will execute before Show() is called, but be warned: calling msgBox() in show can cause that msgBox() to
execute before Load() is finished. It is generally a bad idea to depend on event ordering between Load(),
Show(), and similar.
End Class
Use &, not + for string concatenation. Strings should be studied in some detail as they are widely used.
Never use Application.DoEvents. Pay attention to the 'Caution'. When you reach a point where this seems like
something you must use, ask.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 115
For example it could be used to show the user the time of day periodically.
But this timer is not suited for timing. An example would be using it for a countdown. In this example we will
simulate a countdown to three minutes. This may very well be one of the most boringly important examples here.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 116
After button1 is clicked, about three minutes pass and label1 shows the results. Does label1 show 180? Probably
not. On my machine it showed 182.5!
The reason for the discrepancy is in the documentation, "The Windows Forms Timer component is single-threaded,
and is limited to an accuracy of 55 milliseconds." This is why it shouldn't be used for timing.
By using the timer and stopwatch a little differently we can obtain better results.
There are other timers that can be used as needed. This search should help in that regard.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 117
Chapter 36: JIT compiler
JIT compilation, or just-in-time compilation, is an alternative approach to interpretation of code or ahead-of-time
compilation. JIT compilation is used in the .NET framework. The CLR code (C#, F#, Visual Basic, etc.) is first compiled
into something called Interpreted Language, or IL. This is lower level code that is closer to machine code, but is not
platform specific. Rather, at runtime, this code is compiled into machine code for the relevant system.
using System;
namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World");
}
}
}
// --- The following custom attribute is added automatically, do not uncomment -------
// .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype
[mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 )
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 119
Generated with MS ILDASM tool (IL disassembler)
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 120
Chapter 37: CLR
Section 37.1: An introduction to Common Language Runtime
The Common Language Runtime (CLR) is a virtual machine environment and part of the .NET Framework. It
contains:
A portable bytecode language called Common Intermediate Language (abbreviated CIL, or IL)
A Just-In-Time compiler that generates machine code
A tracing garbage collector that provides automatic memory management
Support for lightweight sub-processes called AppDomains
Security mechanisms through the concepts of verifiable code and trust levels
Code that runs in the CLR is referred to as managed code to distinguish it from code running outside the CLR
(usually native code) which is referred to as unmanaged code. There are various mechanisms that facilitate
interoperability between managed and unmanaged code.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 121
Chapter 38: TPL Dataflow
Section 38.1: Asynchronous Producer Consumer With A
Bounded BuerBlock
var bufferBlock = new BufferBlock<int>(new DataflowBlockOptions
{
BoundedCapacity = 1000
});
while (!cancellationToken.IsCancellationRequested)
{
var value = random.Next();
await bufferBlock.SendAsync(value, cancellationToken);
}
});
block.Complete(); // Tell the block to complete and stop accepting new items
await block.Completion; // Asynchronously wait until all items completed processingu
// Create a block the accepts a uri and returns its contents as a string
var downloaderBlock = new TransformBlock<string, string>(
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 122
async uri => await httpClient.GetStringAsync(uri));
// Create a block that accepts the content and prints it to the console
var printerBlock = new ActionBlock<string>(
contents => Console.WriteLine(contents));
// Post urls to the first block which will pass their contents to the second one.
downloaderBlock.Post("http://youtube.com");
downloaderBlock.Post("http://github.com");
downloaderBlock.Post("http://twitter.com");
class Program
{
private static BufferBlock<double> buffer = new BufferBlock<double>();
static void Main (string[] args)
{
//start a task that will every 1 second post a value from the producer to buffer block
var producerTask = Task.Run(async () =>
{
var producer = new Producer();
while(true)
{
buffer.Post(producer.Produce());
await Task.Delay(1000);
}
});
//start a task that will recieve values from bufferblock and consume it
var consumerTask = Task.Run(() =>
{
var consumer = new Consumer();
while(true)
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 123
{
consumer.Consume(buffer.Receive());
}
});
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 124
Chapter 39: Threading
Section 39.1: Accessing form controls from other threads
If you want to change an attribute of a control such as a textbox or label from another thread than the GUI thread
that created the control, you will have to invoke it or else you might get an error message stating:
"Cross-thread operation not valid: Control 'control_name' accessed from a thread other than the thread it
was created on."
Using this example code on a system.windows.forms form will cast an exception with that message:
Instead when you want to change a textbox's text from within a thread that doesn't own it use Control.Invoke or
Control.BeginInvoke. You can also use Control.InvokeRequired to check if invoking the control is necessary.
If you need to do this often, you can write an extension for invokeable objects to reduce the amount of code
necessary to make this check:
And updating the textbox from any thread becomes a bit simpler:
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 125
Be aware that Control.BeginInvoke as used in this example is asynchronous, meaning that code coming after a call
to Control.BeginInvoke can be run immedeately after, whether or not the passed delegate has been executed yet.
If you need to be sure that textBox1 is updated before continuing, use Control.Invoke instead, which will block the
calling thread until your delegate has been executed. Do note that this approach can slow your code down
significantly if you make many invoke calls and note that it will deadlock your application if your GUI thread is
waiting for the calling thread to complete or release a held resource.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 126
Chapter 40: Process and Thread anity
setting
Parameter Details
integer that describes the set of processors on which the process is allowed to run. For example, on a 8
affinity processor system if you want your process to be executed only on processors 3 and 4 than you choose
affinity like this : 00001100 which equals 12
return processorAffinity;
}
Example of usage :
Console.ReadKey();
}
// Output:
// Process Test.vshost Affinity Mask is : 11111111
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 127
{
Process myProcess = GetProcessByName(ref processName);
Example of usage :
Console.ReadKey();
}
// Output :
// Process Test.vshost Old Affinity Mask is : 11111111
// Process Test.vshost New Affinity Mask is : 10101010
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 128
Chapter 41: Parallel processing using .Net
framework
This Topic is about Multi core programming using Task Parallel Library with .NET framework. The task parallel
library allows you to write code which is human readable and adjusts itself with the number of Cores available. So
you can be sure that your software would auto-upgrade itself with the upgrading environment.
//Sequential version
Process(item);
// Parallel equivalent
The above mentioned Parallel.ForEach construct utilizes the multiple cores and thus enhances the performance in
the same fashion.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 129
Chapter 42: Task Parallel Library (TPL)
Section 42.1: Basic producer-consumer loop
(BlockingCollection)
var collection = new BlockingCollection<int>(5);
var random = new Random();
It is worth noting that if you do not call collection.CompleteAdding();, you are able to keep adding to the
collection even if your consumer task is running. Just call collection.CompleteAdding(); when you are sure there
are no more additions. This functionality can be used to make a Multiple Producer to a Single Consumer pattern
where you have multiple sources feeding items into the BlockingCollection and a single consumer pulling items out
and doing something with them. If your BlockingCollection is empty before you call complete adding, the
Enumerable from collection.GetConsumingEnumerable() will block until a new item is added to the collection or
BlockingCollection.CompleteAdding(); is called and the queue is empty.
Task.WaitAll(producerTask, consumerTask);
Console.WriteLine("Everything completed!");
try
{
Parallel.Invoke(actions);
}
catch(AggregateException ex)
{
foreach(var inner in ex.InnerExceptions)
Console.WriteLine("Task failed: " + inner.Message);
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 130
}
return sum;
});
If the Task execute asynchronously than awaiting the Task returns it's result.
using System.Threading;
int Foo()
{
int total = 0;
var numbers = Enumerable.Range(1, 10000).ToList();
Parallel.ForEach(numbers,
() => 0, // initial value,
(num, state, localSum) => num + localSum,
localSum => Interlocked.Add(ref total, localSum));
return total; // total = 50005000
}
using System.Threading;
int Foo()
{
int total = 0;
Parallel.For(1, 10001,
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 131
() => 0, // initial value,
(num, state, localSum) => num + localSum,
localSum => Interlocked.Add(ref total, localSum));
return total; // total = 50005000
}
Console.WriteLine("Starting task...");
task.Start();
task.Wait();
Console.WriteLine("Task completed!");
Console.WriteLine("Starting task...");
var task = Task.Run(() =>
{
Console.WriteLine("Task code starting...");
Thread.Sleep(2000);
Console.WriteLine("...task code ending!");
});
task.Wait();
Console.WriteLine("Task completed!");
Note that only in the first case it is necessary to explicitly invoke Start.
void Main()
{
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 132
AsyncLocal<string> user = new AsyncLocal<string>();
user.Value = "initial user";
// this does not affect other tasks - values are local relative to the branches of execution flow
Task.Run(() => user.Value = "user from another task");
Task.Run(() =>
{
// outputs "user from task1" - value has flown from main method to task1
// than value was changed and flown to this task.
Console.WriteLine(user.Value);
}).Wait();
});
task1.Wait();
// outputs "initial user" - changes do not propagate back upstream the execution flow
Console.WriteLine(user.Value);
}
Note: As can be seen from the example above AsynLocal.Value has copy on read semantic, but if you flow some
reference type and change its properties you will affect other tasks. Hence, best practice with AsyncLocal is to use
value types or immutable types.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 133
foreach(var task in tasks)
Console.WriteLine(task.Result);
while(pendingTasks.Length > 0)
{
var finishedTask = pendingTasks[Task.WaitAny(pendingTasks)];
Console.WriteLine("Task {0} finished", finishedTask.Result);
pendingTasks = pendingTasks.Except(new[] {finishedTask}).ToArray();
}
Task.WaitAll(allTasks);
Note: The final WaitAll is necessary becasue WaitAny does not cause exceptions to be observed.
Console.WriteLine("Starting tasks...");
try
{
Task.WaitAll(task1, task2);
}
catch(AggregateException ex)
{
Console.WriteLine("Task(s) failed!");
foreach(var inner in ex.InnerExceptions)
Console.WriteLine(inner.Message);
}
Console.WriteLine("Starting tasks...");
while(tasks.All(task => !task.IsCompleted));
cancellationTokenSource.Cancel();
try
{
task.Wait();
}
catch(AggregateException ex)
{
ex.Handle(inner => inner is OperationCanceledException);
}
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 135
int i = 1;
var myCancellationToken = (CancellationToken)state;
while(!myCancellationToken.IsCancellationRequested)
{
Console.Write("{0} ", i++);
Thread.Sleep(1000);
}
Console.WriteLine($"{Environment.NewLine}Ouch, I have been cancelled!!");
throw new OperationCanceledException(myCancellationToken);
Note how the cancellation token is passed to the task constructor in the cancellationToken parameter. This is
needed so that the task transitions to the Canceled state, not to the Faulted state, when
ThrowIfCancellationRequested is invoked. Also, for the same reason, the cancellation token is explicitly supplied
in the constructor of OperationCanceledException in the second case.
await Task.WhenAll(tasks);
Console.WriteLine("All tasks finished!");
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 136
Chapter 43: Task Parallel Library (TPL) API
Overviews
Section 43.1: Perform work in response to a button click and
update the UI
This example demonstrates how you can respond to a button click by performing some work on a worker thread
and then update the user interface to indicate completion
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 137
Chapter 44: Synchronization Contexts
Section 44.1: Execute code on the UI thread after performing
background work
This example shows how to update a UI component from a background thread by using a SynchronizationContext
In this example, if you tried to directly update MyTextBox.Text inside the for loop, you would get a threading error.
By posting the UpdateCallback action to the SynchronizationContext, the text box is updated on the same thread
as the rest of the UI.
In practice, progress updates should be performed using an instance of System.IProgress<T>. The default
implementation System.Progress<T> automatically captures the synchronisation context it is created on.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 138
Chapter 45: Memory management
Section 45.1: Use SafeHandle when wrapping unmanaged
resources
When writing wrappers for unmanaged resources, you should subclass SafeHandle rather than trying to implement
IDisposable and a finalizer yourself. Your SafeHandle subclass should be as small and simple as possible to
minimize the chance of a handle leak. This likely means that your SafeHandle implementation would an internal
implementation detail of a class which wraps it to provide a usable API. This class ensures that, even if a program
leaks your SafeHandle instance, your unmanaged handle is released.
using System.Runtime.InteropServices;
Disclaimer: This example is an attempt to show how to guard a managed resource with SafeHandle which
implements IDisposable for you and configures finalizers appropriately. It is very contrived and likely pointless to
allocate a chunk of memory in this manner.
When handling unmanaged resources, you should make sure that they are properly disposed. You can do this by
explicitly calling Dispose() in a finally block, or with a using statement.
StreamReader sr;
string textFromFile;
string filename = "SomeFile.txt";
try
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 139
{
sr = new StreamReader(filename);
textFromFile = sr.ReadToEnd();
}
finally
{
if (sr != null) sr.Dispose();
}
or
string textFromFile;
string filename = "SomeFile.txt";
The latter is the preferred method, and is automatically expanded to the former during compilation.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 140
Chapter 46: Garbage Collection
In .Net, objects created with new() are allocated on the managed heap. These objects are never explicitly finalized
by the program that uses them; instead, this process is controlled by the .Net Garbage Collector.
Some of the examples below are "lab cases" to show the Garbage Collector at work and some significant details of
its behavior, while other focus on how to prepare classes for proper handling by the Garbage Collector.
~FinalizableObject()
{
Console.WriteLine("Instance finalized");
}
}
<namespace>.FinalizableObject initialized
If nothing else happens, the object is not finalized until the program ends (which frees all objects on the managed
heap, finalizing these in the process).
<namespace>.FinalizableObject initialized
<namespace>.FinalizableObject finalized
This time, as soon as the Garbage Collector was invoked, the unused (aka "dead") object was finalized and freed
from the managed heap.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 141
In the following example (for convenience, FinalizableObject1 and FinalizableObject2 are subclasses of
FinalizableObject from the example above and thus inherit the initialization / finalization message behavior):
<namespace>.FinalizableObject1 initialized
<namespace>.FinalizableObject2 initialized
<namespace>.FinalizableObject1 finalized
At the time when the Garbage Collector is invoked, FinalizableObject1 is a dead object and gets finalized, while
FinalizableObject2 is a live object and it is kept on the managed heap.
<namespace>.FinalizedObject1 initialized
<namespace>.FinalizedObject2 initialized
<namespace>.FinalizedObject1 finalized
<namespace>.FinalizedObject2 finalized
The two objects are finalized and freed from the managed heap despite referencing each other (because no other
reference exists to any of them from an actually live object).
<namespace>.FinalizableObject initialized
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 142
<namespace>.FinalizableObject finalized
The object is freed from the managed heap despite being referenced by the WeakReference variable (still in scope
when the Garbage collector was invoked).
Consequence #1: at any time, it is unsafe to assume whether a WeakReference target is still allocated on the
managed heap or not.
Consequence #2: whenever a program needs to access the target of a Weakreference, code should be provided for
both cases, of the target being still allocated or not. The method to access the target is TryGetTarget:
The generic version of WeakReference is available since .Net 4.5. All framework versions provide a non-generic,
untyped version that is built in the same way and checked as follows:
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 143
One scenario is a program calling Dispose() on objects it explicitly creates:
Another scenario is declaring a class to be instantiated by the framework. In this case the new class usually inherits
a base class, for instance in MVC one creates a controller class as a subclass of System.Web.Mvc.ControllerBase.
When the base class implements IDisposable interface, this is a good hint that Dispose() would be invoked properly
by the framework - but again there is no strong guarantee.
Thus Dispose() is not a substitute for a finalizer; instead, the two should be used for different purposes:
A finalizer eventually frees resources to avoid memory leaks that would occur otherwise
Dispose() frees resources (possibly the same ones) as soon as these are no longer needed, to ease pressure
on overall memory allocation.
One solution is writing the cleanup code in such a way that running it once or twice would produce the same result
as running it only once. Feasibility depends on the nature of the cleanup, for instance:
Closing an already closed database connection would probably have no effect so it works
Updating some "usage count" is dangerous and would produce a wrong result when called twice instead of
once.
A safer solution is ensuring by design that the cleanup code is called once and only once whatever the external
context. This can be achieved the "classic way" using a dedicated flag:
~DisposableFinalizable1() { Cleanup(); }
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 144
{
if(!disposed)
{
// Actual code to release resources gets here, then
disposed = true;
}
}
}
Alternately, the Garbage Collector provides a specific method SuppressFinalize() that allows skipping the finalizer
after Dispose has been invoked:
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 145
Chapter 47: Exceptions
Section 47.1: Catching and rethrowing caught exceptions
When you want to catch an exception and do something, but you can't continue execution of the current block of
code because of the exception, you may want to rethrow the exception to the next exception handler in the call
stack. There are good ways and bad ways to do this.
// remember that the stack trace of the outer Exception will point to the
// next line
// you'll need to examine the InnerException property to get the stack trace
// to the line that actually started the problem
You can filter by exception type and even by exception properties (new in C# 6.0, a bit longer available in VB.NET
(citation needed)):
Documentation/C#/new features
It can be utilized to free or clean up resources acquired in the try { ... } block safely.
try
{
fileStream = File.Open(filename);
}
catch (FileNotFoundException)
{
Console.WriteLine("File '{0}' could not be found.", filename);
}
finally
{
if (fileStream != null)
{
fileStream.Dispose();
}
}
This is similar to using a simple if but does not unwind the stack if the condition inside the when is not met.
Example
try
{
// ...
}
catch (Exception e) when (e.InnerException != null) // Any condition can go in here.
{
// ...
}
The same info can be found in the C# 6.0 Features here: Exception filters
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 147
Section 47.4: Rethrowing an exception within a catch block
Within a catch block the throw keyword can be used on its own, without specifying an exception value, to rethrow
the exception which was just caught. Rethrowing an exception allows the original exception to continue up the
exception handling chain, preserving its call stack or associated data:
try {...}
catch (Exception ex) {
// Note: the ex variable is *not* used
throw;
}
A common anti-pattern is to instead throw ex, which has the effect of limiting the next exception handler's view of
the stack trace:
try {...}
catch (Exception ex) {
// Note: the ex variable is thrown
// future stack traces of the exception will not see prior calls
throw ex;
}
In general using throw ex isn't desirable, as future exception handlers which inspect the stack trace will only be
able to see calls as far back as throw ex. By omitting the ex variable, and using the throw keyword alone the
original exception will "bubble-up".
using System.Runtime.ExceptionServices;
void Main()
{
ExceptionDispatchInfo capturedException = null;
try
{
throw new Exception();
}
catch (Exception ex)
{
capturedException = ExceptionDispatchInfo.Capture(ex);
}
Foo(capturedException);
}
if (capturedException != null)
{
// Exception stack trace will show it was thrown from Main() and not from Foo()
exceptionDispatchInfo.Throw();
}
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 148
}
The caller can handle these exceptions by "catching" them, and should only do so when:
It should be noted that choosing not to catch an exception is perfectly valid if the intention is for it to be handled at
a higher level.
Catching an exception is done by wrapping the potentially-throwing code in a try { ... } block as follows, and
catching the exceptions it's able to handle in a catch (ExceptionType) { ... } block:
Stream fileStream;
try
{
fileStream = File.Open(filename);
}
catch (FileNotFoundException)
{
Console.WriteLine("File '{0}' could not be found.", filename);
}
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 149
Chapter 48: System.Diagnostics
Section 48.1: Run shell commands
string strCmdText = "/C copy /b Image1.jpg + Archive.rar Image2.jpg";
System.Diagnostics.Process.Start("CMD.exe",strCmdText);
p.Start();
Usage
Output
string currentUsers = "USERNAME SESSIONNAME ID STATE IDLE TIME LOGON TIME Joe.Bloggs ica-cgp#0 2
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 150
Active 24692+13:29 25/07/2016 07:50 Jim.McFlannegan ica-cgp#1 3 Active . 25/07/2016 08:33
Andy.McAnderson ica-cgp#2 4 Active . 25/07/2016 08:54 John.Smith ica-cgp#4 5 Active 14 25/07/2016
08:57 Bob.Bobbington ica-cgp#5 6 Active 24692+13:29 25/07/2016 09:05 Tim.Tom ica-cgp#6 7 Active .
25/07/2016 09:08 Bob.Joges ica-cgp#7 8 Active 24692+13:29 25/07/2016 09:13"
On some occasions, access to the server in question may be restricted to certain users. If you have the login
credentials for this user, then it is possible to send queries with this method:
Verb = "runas",
Domain = "doman1.co.za",
UserName = "administrator",
Password = GetPassword()
};
p.Start();
return ss;
}
Notes
Both of the above methods will return a concatenation of STDOUT and STDERR, as OutputDataReceived and
ErrorDataReceived are both appending to the same variable - cmdOut.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 151
Section 48.3: Stopwatch
This example shows how Stopwatch can be used to benchmark a block of code.
using System;
using System.Diagnostics;
public Benchmark()
{
sw = Stopwatch.StartNew();
}
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 152
Chapter 49: Encryption / Cryptography
Section 49.1: Encryption and Decryption using Cryptography
(AES)
Decryption Code
cipherText = Encoding.Unicode.GetString(ms.ToArray());
}
}
return cipherText;
}
Encryption Code
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 153
cipherText = Convert.ToBase64String(ms.ToArray());
}
}
return cipherText;
}
Usage
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 154
} finally {
aesAlg?.Clear();
}
return encrypt.ToArray();
}
Usage
Note:
Section 49.3: Encrypt and decrypt data using AES (in C#)
using System;
using System.IO;
using System.Security.Cryptography;
namespace Aes_Example
{
class AesExample
{
public static void Main()
{
try
{
string original = "Here is some data to encrypt!";
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 155
{
// Encrypt the string to an array of bytes.
byte[] encrypted = EncryptStringToBytes_Aes(original,
myAes.Key,
myAes.IV);
byte[] encrypted;
encrypted = msEncrypt.ToArray();
}
}
}
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 156
// Return the encrypted bytes from the memory stream.
return encrypted;
}
return plaintext;
}
}
}
It is a console demo application, showing how to encrypt a string by using the standard AES encryption, and how to
decrypt it afterwards.
(AES = Advanced Encryption Standard, a specification for the encryption of electronic data established by the U.S.
National Institute of Standards and Technology (NIST) in 2001 which is still the de-facto standard for symmetric
encryption)
Notes:
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 157
In a real encryption scenario, you need to choose a proper cipher mode (can be assigned to the Mode
property by selecting a value from the CipherMode enumeration). Never use the CipherMode.ECB (electronic
codebook mode), since this procuces a weak cypher stream
To create a good (and not a weak) Key, either use a cryptographic random generator or use the example
above (Create a Key from a Password). The recommended KeySize is 256 bit. Supported key sizes are
available via the LegalKeySizes property.
To initialize the initialization vector IV, you can use a SALT as shown in the example above (Random SALT)
Supported block sizes are available via the SupportedBlockSizes property, the block size can be assigned via
the BlockSize property
try
{
Console.WriteLine("Creating a key with PasswordDeriveBytes...");
Console.WriteLine("Operation complete.");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
// Clear the buffers
ClearBytes(pwd);
ClearBytes(salt);
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 158
// Clear the key.
tdes.Clear();
}
Console.ReadLine();
}
/// <summary>
/// Generates a random salt value of the specified length.
/// </summary>
public static byte[] CreateRandomSalt(int length)
{
// Create a buffer
byte[] randBytes;
if (length >= 1)
{
randBytes = new byte[length];
}
else
{
randBytes = new byte[1];
}
/// <summary>
/// Clear the bytes in a buffer so they can't later be read from memory.
/// </summary>
public static void ClearBytes(byte[] buffer)
{
// Check arguments.
if (buffer == null)
{
throw new ArgumentNullException("buffer");
}
#endregion
}
It is a console demo, and it shows how to create a secure key based on a user-defined password, and how to create
a random SALT based on the cryptographic random generator.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 159
Notes:
The built-in function PasswordDeriveBytes uses the standard PBKDF1 algorithm to generate a key from the
password. Per default, it uses 100 iterations to generate the key to slow down brute force attacks. The SALT
generated randomly further strenghens the key.
The function CryptDeriveKey converts the key generated by PasswordDeriveBytes into a key compatible
with the specified encryption algorithm (here "TripleDES") by using the specified hash algorithm (here
"SHA1"). The keysize in this example is 192 bytes, and the initialization vector IV is taken from the triple-DES
crypto provider
Usually, this mechanism is used to protect a stronger random generated key by a password, which encrypts
large amount of data. You can also use it to provide multiple passwords of different users to give access to
the same data (being protected by a different random key).
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 160
Chapter 50: Work with SHA1 in C#
in this project you see how to work with SHA1 cryptographic hash function. for example get hash from string and
how to crack SHA1 hash.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 161
Chapter 51: Unit testing
Section 51.1: Adding MSTest unit testing project to an existing
solution
Right click on the solution, Add new project
From the Test section, select an Unit Test Project
Pick a name for the assembly - if you are testing project Foo, the name can be Foo.Tests
Add a reference to the tested project in the unit test project references
[TestClass]
public class FizzBuzzFixture
{
[TestMethod]
public void Test1()
{
//arrange
var solver = new FizzBuzzSolver();
//act
var result = solver.FizzBuzz(1);
//assert
Assert.AreEqual("1",result);
}
}
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 162
Chapter 52: Write to and read from StdErr
stream
Section 52.1: Write to standard error output using Console
var sourceFileName = "NonExistingFile";
try
{
System.IO.File.Copy(sourceFileName, "DestinationFile");
}
catch (Exception e)
{
var stdErr = Console.Error;
stdErr.WriteLine($"Failed to copy '{sourceFileName}': {e.Message}");
}
};
process.ErrorDataReceived += (s, e) => errors.AppendLine(e.Data);
process.Start();
process.BeginErrorReadLine();
process.WaitForExit();
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 163
Chapter 53: Upload file and POST data to
webserver
Section 53.1: Upload file with WebRequest
To send a file and form data in single request, content should have multipart/form-data type.
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading.Tasks;
await writer.FlushAsync();
using (var fileStream = File.OpenRead(filename))
await fileStream.CopyToAsync(requestStream);
await writer.WriteAsync($"\r\n--{boundary}--\r\n");
}
Usage:
var response = await uploader.UploadFile("< YOUR URL >", "< PATH TO YOUR FILE >",
new Dictionary<string, object>
{
{"Comment", "test"},
{"Modified", DateTime.Now }
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 164
});
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 165
Chapter 54: Networking
Section 54.1: Basic TCP chat (TcpListener, TcpClient,
NetworkStream)
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
class TcpChat
{
static void Main(string[] args)
{
if(args.Length == 0)
{
Console.WriteLine("Basic TCP chat");
Console.WriteLine();
Console.WriteLine("Usage:");
Console.WriteLine("tcpchat server <port>");
Console.WriteLine("tcpchat client <url> <port>");
return;
}
try
{
Run(args);
}
catch(IOException)
{
Console.WriteLine("--- Connection lost");
}
catch(SocketException ex)
{
Console.WriteLine("--- Can't connect: " + ex.Message);
}
}
if(args[0].StartsWith("s", StringComparison.InvariantCultureIgnoreCase))
{
var port = int.Parse(args[1]);
var listener = new TcpListener(IPAddress.Any, port);
listener.Start();
Console.WriteLine("--- Waiting for a connection...");
client = listener.AcceptTcpClient();
}
else
{
var hostName = args[1];
var port = int.Parse(args[2]);
client = new TcpClient();
client.Connect(hostName, port);
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 166
}
stream = client.GetStream();
Console.WriteLine("--- Connected. Start typing! (exit with Ctrl-C)");
while(true)
{
if(Console.KeyAvailable)
{
var lineToSend = Console.ReadLine();
var bytesToSend = encoding.GetBytes(lineToSend + "\r\n");
stream.Write(bytesToSend, 0, bytesToSend.Length);
stream.Flush();
}
if (stream.DataAvailable)
{
var receivedBytesCount = stream.Read(buffer, 0, buffer.Length);
var receivedString = encoding.GetString(buffer, 0, receivedBytesCount);
Console.Write(receivedString);
}
}
}
}
using System;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Sockets;
class SntpClient
{
const int SntpPort = 123;
static DateTime BaseDate = new DateTime(1900, 1, 1);
double localTimeZoneInHours = 0;
if(args.Length > 1)
localTimeZoneInHours = double.Parse(args[1], CultureInfo.InvariantCulture);
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 167
udpClient.Send(
dgram: sntpRequest,
bytes: sntpRequest.Length,
hostname: args[0],
port: SntpPort);
byte[] sntpResponse;
try
{
IPEndPoint remoteEndpoint = null;
sntpResponse = udpClient.Receive(ref remoteEndpoint);
}
catch(SocketException)
{
Console.WriteLine("*** No response received from the server");
return;
}
uint numberOfSeconds;
if(BitConverter.IsLittleEndian)
numberOfSeconds = BitConverter.ToUInt32(
sntpResponse.Skip(40).Take(4).Reverse().ToArray()
,0);
else
numberOfSeconds = BitConverter.ToUInt32(sntpResponse, 40);
Console.WriteLine(
$"Current date in server: {date:yyyy-MM-dd HH:mm:ss}
UTC{localTimeZoneInHours:+0.#;-0.#;.}");
}
}
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 168
Chapter 55: HTTP servers
Section 55.1: Basic read-only HTTP file server (ASP.NET Core)
1 - Create an empty folder, it will contain the files created in the next steps.
2 - Create a file named project.json with the following content (adjust the port number and rootDirectory as
appropriate):
{
"dependencies": {
"Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final",
"Microsoft.AspNet.StaticFiles": "1.0.0-rc1-final"
},
"commands": {
"web": "Microsoft.AspNet.Server.Kestrel --server.urls http://localhost:60000"
},
"frameworks": {
"dnxcore50": { }
},
"fileServer": {
"rootDirectory": "c:\\users\\username\\Documents"
}
}
using System;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.FileProviders;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.StaticFiles;
using Microsoft.Extensions.Configuration;
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 169
app.UseStaticFiles(options);
}
}
Note: These commands need to be run only once. Use dnvm list to check the actual number of the latest installed
version of the core CLR.
5 - Start the server with: dnx web. Files can now be requested at http://localhost:60000/path/to/file.ext.
For simplicity, filenames are assumed to be all ASCII (for the filename part in the Content-Disposition header) and
file access errors are not handled.
For simplicity, filenames are assumed to be all ASCII (for the filename part in the Content-Disposition header) and file
access errors are not handled.
using System;
using System.IO;
using System.Net;
class HttpFileServer
{
private static HttpListenerResponse response;
private static HttpListener listener;
private static string baseFilesystemPath;
if(args.Length < 2)
{
Console.WriteLine("Basic read-only HTTP file server");
Console.WriteLine();
Console.WriteLine("Usage: httpfileserver <base filesystem path> <port>");
Console.WriteLine("Request format: http://url:port/path/to/file.ext");
return;
}
baseFilesystemPath = Path.GetFullPath(args[0]);
var port = int.Parse(args[1]);
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 170
listener = new HttpListener();
listener.Prefixes.Add("http://*:" + port + "/");
listener.Start();
if (request.HttpMethod.ToUpper() != "GET")
{
SendErrorResponse(405, "Method must be GET");
continue;
}
response.OutputStream.Close();
response = null;
Console.WriteLine(" Ok!");
}
}
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 171
static void SendErrorResponse(int statusCode, string statusResponse)
{
response.ContentLength64 = 0;
response.StatusCode = statusCode;
response.StatusDescription = statusResponse;
response.OutputStream.Close();
Console.WriteLine("*** Sent error: {0} {1}", statusCode, statusResponse);
}
}
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 172
Chapter 56: HTTP clients
Section 56.1: Reading GET response as string using
System.Net.HttpClient
HttpClient is available through NuGet: Microsoft HTTP Client Libraries.
class HttpGet
{
private static async Task DownloadAsync(string fromUrl, string toFile)
{
using (var fileStream = File.OpenWrite(toFile))
{
using (var httpClient = new HttpClient())
{
Console.WriteLine("Connecting...");
using (var networkStream = await httpClient.GetStreamAsync(fromUrl))
{
Console.WriteLine("Downloading...");
await networkStream.CopyToAsync(fileStream);
await fileStream.FlushAsync();
}
}
}
}
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 173
Console.WriteLine("--- Error: " +
(ex.InnerException?.Message ?? ex.Message));
}
}
static async Task Run(string[] args)
{
if (args.Length < 2)
{
Console.WriteLine("Basic HTTP downloader");
Console.WriteLine();
Console.WriteLine("Usage: httpget <url>[<:port>] <file>");
return;
}
Console.WriteLine("Done!");
}
}
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 174
byte[] bytes = Encoding.UTF8.GetBytes(requestBodyString);
Stream stream = request.GetRequestStream();
stream.Write(bytes, 0, bytes.Length);
stream.Close();
byte[] responseBody;
byte[] requestBodyBytes = Encoding.UTF8.GetBytes(requestBodyString);
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 175
Chapter 57: Serial Ports
Section 57.1: Basic operation
var serialPort = new SerialPort("COM1", 9600, Parity.Even, 8, StopBits.One);
serialPort.Open();
serialPort.WriteLine("Test data");
string response = serialPort.ReadLine();
Console.WriteLine(response);
serialPort.Close();
namespace TextEchoService
{
class Program
{
static void Main(string[] args)
{
var serialPort = new SerialPort("COM1", 9600, Parity.Even, 8, StopBits.One);
serialPort.Open();
string message = "";
while (message != "quit")
{
message = serialPort.ReadLine();
serialPort.WriteLine(message);
}
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 176
serialPort.Close();
}
}
}
namespace AsyncReceiver
{
class Program
{
const byte STX = 0x02;
const byte ETX = 0x03;
const byte ACK = 0x06;
const byte NAK = 0x15;
static ManualResetEvent terminateService = new ManualResetEvent(false);
static readonly object eventLock = new object();
static List<byte> unprocessedBuffer = null;
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 177
}
}
}
static void ErrorReceivedHandler(object sender, SerialErrorReceivedEventArgs e)
{
lock (eventLock)
if (e.EventType == SerialError.TXFull)
{
Console.WriteLine("Error: TXFull. Can't handle this!");
terminateService.Set();
}
else
{
Console.WriteLine("Error: {0}. Resetting everything", e.EventType);
var port = (SerialPort)sender;
port.DiscardInBuffer();
port.DiscardOutBuffer();
unprocessedBuffer = null;
port.Write(new byte[] { NAK }, 0, 1);
}
}
This program waits for messages enclosed in STX and ETX bytes and outputs the text coming between them.
Everything else is discarded. On write buffer overflow it stops. On other errors it reset input and output buffers and
waits for further messages.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 178
The SerialPort.DataReceived event may happen only after several messages have been sent by the
other end.
LearnLoner.com
GoalKicker.com – .NET Framework Notes for Professionals 179