Versions

C# 1.0 - Jan 2002 - .Net Framework 1.0
C# 1.2 - Apr 2003 - .Net Framework 1.1
C# 2.0 - Nov 2005 - .Net Framework 2.0
C# 3.0 - Nov 2007 - .Net Framework 2.0, 3.0, 3.5
C# 4.0 - Apr 2010 - .Net Framework 4.0
C# 5.0 - Aug 2012 - .Net Framework 4.5
C# 6.0 - Jul 2015 - .Net Framework 4.6
C# 7.0 - Mar 2017 - .Net Framework 4.7
C# 7.1 - Aug 2017 - .Net Core 2.0
C# 7.2 - Nov 2017 - ?
C# 7.3 - May 2018 - .Net Core 2.1, .Net Core 2.2, .Net Framework 4.8
C# 8.0 - Sep 2019 - .Net Core 3.0, .Net Core 3.1
C# 9.0 - Nov 2020 - .Net 5
C# 10.0 - Nov 2021 - .Net 6, .Net 6.1
C# 11.0 - Nov 2022 - .Net 7
C# 12.0 - Nov 2023 - .Net 8

[C# Versions at C# Corner]

Hello, World


using System;

static class Exercise01
{
    static void Main(string[] args)
    {
        Console.WriteLine("Hello, World");
    }
}

This can be compiled and run from the command line.
Locate your csc.exe file (example location C:\Windows\Microsoft.Net\Framework\{version}\csc.exe or \bin\csc.exe)

 #in command prompt
 #[csc location] [output exe or dll] [name of exe] [file(s) to compile]
{full path to csc.exe} /t:exe /out:my_program.exe my_program.cs

The to run

my_program.exe

once you have unit tests setup, you can run them from command prompt

dotnet test

basic i/o

Console.WriteLine("Enter a number: ");
var input = Console.ReadLine(); //get what the user entered

Braces

[Identation and Brace Styles]

Allman Style

The standard for C#.


class MyClass
{
    void MyMethod()
    {
        DoSomething();
    }
}

1TBS

The standard for Javascript.
1TBS stands for "1 true brace style".


class MyClass {
    void MyMethod() {
        DoSomething();
    }
}

Namespaces

Namespaces have a nested structure. Each segment of a namespace like "System.Collections.Generic" is a namespace:
- System
- Collections within System
- Generic within Collections

Nested namespaces may be declared two ways:

namespace System.Collections { }
//or
namespace System 
{
    namespace Collections { }
}

The fully qualified name of a type includes the full namespaces it is within:
- System.Collections.Generic.List<T>

Fully qualified names must be unique within a program.

You cannot declare a namespace within a class, interface, or struct.

C# 10 (.Net 6) File-scoped namespaces

namespace A.B.C;

public class MyClass 
{
}
One allowed per file. Must be above all type declarations.

Naming Convention

The conventional organization of a project is for the namespaces to correspond to the file structure.
- Files at the root of the project are in namespace "MyProject".
- Files in folder root/Contracts are in namespace "MySpace.Contracts".
- And so on.

Including Libraries

(Rules determined by experimentation)

Library-A: the .Net library you are making.
Library-B: a third-party library you are using within Library-A.

When making Library-A, you can make use of Library-B internally in your project without making it necessary for users of Library-A to also add a reference to Library-B in their project.

When will users of Library-A need to add a reference to Library-B?
- When calling a Library-A method with Library-B Type arguments.
- When calling a Library-A method that returns a Library-B Type, whether or not they capture the return value.
- When referencing a Library-B Type property or field in a Library-A Type object.

Therefore, third-party dependency considerations should not affect how you divide your namespaces. Merely including the namespace in your project does not incur additional references.

Additionally, Visual Studio IntelliSense will report exactly which library you are missing when you try to use a Library-B type.

When referencing Library-B, they will need to reference the same version that Library-A uses.
Naming Conventions

Case

All names are in CamelCase.

Constants and Readonlys have names in all capitals with snake case, such as MAX_COUNT.

Property names start with a capital letter.
Public field names start with a capital letter.
Private field names start with a lower case letter.

Method names start with a capital letter.

Name

Classes have singular names, such as "Customer" instead of "Customers".

Interface names start with "I", such as "IEnumerable".

Fields that are private even within the object start with an underscore, such as _extraPrivateField.
- This indicates that even within the object, this field should not be touched lightly.
- It may indicate a readonly field.

Boolean properties and fields start with "Is" or "Has", such as "IsActive" or "HasErrors".
Methods that return booleans are named the same way.

Class names should not be the same as their Namespace's name.
It confuses the compiler in some cases about which thing you are referring to.
It requires you to fully-qualify class names all over your code.
- What to call the Namespace, then?
- maybe pluralize the name
- maybe specify the use of the Namespace more abstractly
- maybe don't separate this Namespace out at all

Acronyms

Acronyms are spelled with only their first character capitalized, such as "Xml" or "Tcp".

Verbose Names

It's ok to have long variable/method/class names. For instance, I recommend saying "synchronizationContext" instead of "syncContext" or worse "syncCnt".

The name might be very long, such as "CheckInRemoteCustomerWithoutSynchronizationUsingDurableLayer".
And maybe some of that information can be refactored into parameter types or the class name.
But if it can't, you need it to be in the field or method name.
Don't leave information out just because the name is long.

Verbose names are easier to read and to understand.
Verbose names are part of self-documenting code.

You only have to write it a few times, but you'll have to read it many many times.
Scope

to do

Multiple Scopes

You can create multiple private scopes within one method. One possible use is to ensure that a variable is not used after its purpose is complete.


public void MyMethod()
{
    {
        int a = 5;
    }
    //variable a is not valid here
    
    {
        //variable a is not valid here either
        int b = 7;
    }
}

It is generally recommended to decompose a large method into several smaller methods instead.
It's All Objects

Everything in C# is derived from the Object class.

ValueType is derived from Object.
All value types are derived from ValueType. These types are treated as true value types from other languages (for example, memory is allocated to the stack).
You cannot explicitly derive from ValueType. All structs are derived from ValueType.
Ex: Int32 is a struct derived from ValueType.
Class

Classes define reference-types.
Classes can contain any type of member.
Classes can define the implementation of any type of member.
Classes that are not abstract nor static can be instantiated.


public class MyClass
{
    //fields
    //properties
    //methods
}

See "Members" below for what can be included in a class.

Struct

Structs define value-types (saved in Stack Memory).
...If a struct is stored as a property in a class, it will be stored on the heap with the rest of the object.
Structs cannot define explicit parameterless constructors.
Structs cannot define default field values.
Struct field/properties do not have to be immutable, but that is a common use case.

You cannot set a custom default value for a struct. The default is always "all fields/properties set to their default values".

(.Net 4.6)

Structs can now define explicit parameterless constructors.

(.Net 7.2)

Structs can now be declared readonly. This is now an immutable data type.

public readonly struct Point
{
    //...
}

(C# 11)
The compiler will ensure that all fields of a struct type are initialized to their default value as part of executing a constructor.

Ref Struct

Ref Structs are allocated to stack memory, never to heap memory
Limits:
- cannot be used as the element type of an array
- cannot be used as a field type in a class or non-ref struct
- cannot implement interfaces
- cannot be boxed to System.ValueType or System.Object
- cannot be a type argument
- cannot be captured by a lambda or local function (function nested in another member)
- cannot be used in an async method, but can be used by synchronous methods that return Task or Task<Result>
- cannot be used in iterators

public ref struct MyStruct
{
    public bool IsValid;
    public Span<int> Inputs; //Span<T> can be used within a ref struct
    public Span<int> Outputs;
}

Ref structs can be declared readonly

public readonly ref struct MyStruct
{
}

Readonly

(C# 8)
Mark methods as readonly if they do not modify the state of the struct.

public readonly override string ToString()
{
    return $"({X}, {Y}) is {Distance} from the origin";
}
You'll get a compiler warning if a readonly method accesses a non-readonly method or property accessor.

Mark properties as readonly if they never change state.
The compiler does not assume that Get accessors do not change state, so not having a Set accessor is insufficient.

public readonly double Distance => Math.Sqrt(X * X + Y * Y);
The compiler will assume that all auto-generated Get accessors are readonly.

The compiler enforces the rule that readonly members do not alter state.

Non-Destructive Mutation

(C# 10)

public readonly struct MyStruct
{
    public double X { get; set; }
    public double Y { get; set; }
}

public static void Main()
{
    var a = new MyStruct() { X = 1, Y = 2 };
    var b = a with { X = 3 }; //creates a copy with X=3, Y=2
    var c = b with { X = 4, Y = 5 }; //creates a copy with X=4, Y=5
}

Record Struct

(C# 10)
Record Struct
record structs cannot also be ref structs

Ref Fields

(C# 11)
Structs can include ref fields.
Ref fields can have value Null even if they are value types like Int.

public ref struct MyStruct
{
    private ref int number;
    public int GetNumber()
    {
        if (System.Runtime.CompilerServices.Unsafe.IsNullRef(ref number)) //check for null value
        {
            throw new InvalidOperationException("The number ref field is not initialized.");
        }
        return number;
    }
}

Inline Array

(C# 12)
Inline Arrays aka Fixed Sized Buffers
- a structure that contains a contiguous block of N elements of the same type
- a safe-code equivalent of the fixed buffer declaration
- a struct that contains a single field and does not specify an explicit layout
- can usually be accessed like an array to read and write
- can use the range and index operators

This is an advanced language feature, intended for high-performance scenarios where an inline, contiguous block of elements is faster than other alternative data structures.


[System.Runtime.CompilerServices.InlineArray(10)]
public struct CharBuffer
{
    private char _firstElement; //cannot be a pointer type, can be a reference or value type
}
Interface

Interfaces can contain the signatures of methods, properties, events, or indexes.
Interfaces cannot define the implementation of any type of member.
Interfaces cannot be instantiated.

Interfaces cannot contain static members.
Interface members are implicitly public.

When defining Property signatures, an interface can define read and/or write accessibility by including the get and/or set accessor.

Interfaces can be internal. If so, all its members will also be internal.
Interfaces can be declared within a class.

Default

(C# 8)

Interfaces can define default implementations of their methods.
These default can be overridden by the implementing class.

The intent is that API authors can add more methods to an in-use interface without forcing all users to immediately update their code.

Note that Classes that implement from Interfaces with default implementations DO NOT inherit the implementations.
Thus, the allowance for inheriting from multiple Interfaces continues.
What happens is that the consumer may cast their instance of the Class to the Interface type to access the default implementation.
And there is no compilation error that the new method has not been implemented in the inheriting Class.
- Tested and verified this is how it works, despite conflicting articles from Microsoft.


public interface IInterface
{
    int Random(int a, int b)
    {
        return a + b;
    }
    
    int Subtract(int a, int b);
}

public class MyClass : IInterface
{
    public MyClass()
    {
    }

    public int Subtract(int a, intb)
    {
        return a - b;
    }
}

MyClass instance = new MyClass();
Console.WriteLine(instance.Subtract(10, 2)); //outputs 8
Console.WriteLine(instance.Random(10, 2)); //compilation error
Console.WriteLine((instance as IInterface).Random(10, 2)); //outputs 12

To override a default interface implementation:

public class MyClass : IInterface
{
    public MyClass()
    {
    }

    public int Subtract(int a, intb)
    {
        return a - b;
    }
    
    //Note that the override keyword is not used
    public int Random(int a, int b)
    {
        return 4;
    }
}
You cannot call back to the default implementation, the way you could call back to a base class's implementation.

Interfaces can also now include Static Members to better support this new use case.
Interfaces can also now include Access Modifiers to better support this new user case.

Example of providing a default implementation with settings to make it more widely useful:

private static TimeSpan length = new TimeSpan(365 * 2, 0,0,0); // two years
private static int orderCount = 10;
private static decimal discountPercent = 0.10m;

public static void SetLoyaltyThresholds(TimeSpan ago, int minimumOrders = 10, decimal percentageDiscount = 0.10m)
{
    length = ago;
    orderCount = minimumOrders;
    discountPercent = percentageDiscount;
}

public decimal ComputeLoyaltyDiscount() => DefaultLoyaltyDiscount(this);
protected static decimal DefaultLoyaltyDiscount(ICustomer c)
{
    DateTime start = DateTime.Now - length;
    if ((c.DateJoined < start) && (c.PreviousOrders.Count() > orderCount))
    {
        return discountPercent;
    }
    return 0;
}

Example use case of enabling object composition:

interface ILight ...
interface ITimerLight : ILight ...
interface IBlinkingLight : ILight ...

public class LedLight : IBlinkingLight, ITimerLight, ILight
{
}
Record

(C# 9)

record modifier can be added to a reference type

While records can be mutable, they're primarily intended for supporting immutable data models.
Benefits
- Concise syntax for creating a reference type with immutable properties
- Built-in value equality, concise syntax for non-destructive mutation, formatting for display
- Support for inheritance hierarchies

Reference Type

record class... //synonym to clarify a reference type
//can be simplified to just "record"
public record Person(string FirstName, string LastName);

public record Person
{
    public required string FirstName { get; set; }
    public required string LastName { get; set; }
};

Value Type

record struct... //define a value type with similar functionality

public readonly record struct Point(double X, double Y, double Z);

public record struct Point
{
    public double X { get; init; }
    public double Y { get; init; }
    public double Z { get; init; }
}

Positional properties are immutable in a record class and a readonly record struct. They're mutable in a record struct.

When you declare a primary constructor on a record, the compiler generates public properties for the primary constructor parameters.
The primary constructor parameters to a record are referred to as positional parameters.
The compiler creates positional properties that mirror the primary constructor or positional parameters.

Example of that concise syntax

public record Person(string FirstName, string LastName);

public static void Main()
{
    Person person = new("Jane", "Smith"); //this is Positional Syntax For Property Definition
    Console.WriteLine(person); // "Person { FirstName = Jane, LastName = Smith }"
}

Record Class

(available in C# 9, which means you need .NET 6 or later)

A reference type.
Use value-based equality (unlike classes).

To support value-based comparisons, a record will automatically generate these methods for you: Object.Equals(Object), Equals(T recordType), Object.GetHashCode(), ==, !=, System.IEquatable<T>, Object.ToString()


public record Widget {
    public string Name { get; set; }
}

Optional: be clear that this is a reference type

public record class Widget {
    public string Name { get; set; }
}

Record Struct

(available in C# 10)

Records as a value type.


public record struct Widget {
    public string Name { get; set; }
}
Inheritance

Classes

C# only allows a class to inherit from a single other class.
This avoids ambiguous cases like loops in the inheritance tree, when class D derives from B and C, with B and C both deriving from A.

If class B derives from class A, note that casting a B object to type A does not block usage of the B-specific functionality. It can always be cast back to type B.


class MyClass
{
    private List<string> list = new List<string>();
    
    public IEnumerable<string> MyStrings
    {
        //casting to IEnumerable does not protect your list from editing
        get { return list; }
    }
}

Method Overriding

Overriding instance methods:

class A
{
    public virtual void MyMethod()
    {
    }
}

class B : A
{
    public override void MyMethod()
    {
    }
}

Overriding static methods:

class A
{
    public static void MyMethod()
    {
    }
}

class B : A
{
    public static new void MyMethod()
    {
    }
}

Interfaces

A class can implement from any number of interfaces, because they are just a list of member signatures.

Interfaces can also inherit from interfaces.

Explicit Interface Implementation:
When a class implements more than one interface with members of the same name, you can explicitly state which member is from which interface.

string IEmployee.Name
{
    get { return _name; }
}
Explicit interface implementations cannot be declared public, and they can only be called when the object is cast to that interface type.
It can differentiate between properties, between methods, or between a property and method with a shared name.

Structs

Structs cannot inherit from classes or structs.
Structs can implement interfaces.

Members

Field

a variable declared directly in a class or struct
see Instance Fields vs Static Fields

Property


public int Name
{
    get; //get accessor
    set; //set accessor
}
special methods called accessors provide a flexible mechanism to read and write data

TODO: verify this
By default, the private field linked to "Name" is "_name".
You can reference these implicit private fields, like "public int Age { get => _age; set => _age = value; }"

As of C# 7, property accessors can be expression bodied:

private string name;
public string Name
{
    get => name;
    set => name = value.ToUpperCase();
}

(.Net 9)

Init Only Setters
For properties (and indexers) that are set only during initialization, and cannot be modified after that.

public class MyClass
{
    public int X { get; init; } //get accessor and init accessor
    public int Y { get; init; }
}

var myInstance = new MyClass() { X = 1, Y = 2 }; //X and Y are readonly after this

Method

functions specific to a class

To get reflection information:

using System.Reflection;

public class MyType 
{ 
    public void MyMethod() { }
}

//within method...
Type type = typeof(MyType);
MethodInfo methodInfo = type.GetMethods()[0];

As of C# 6.0 or 7.0, you can format a method as an expression body:

//with expression body
public class Foo
{
    public string Name { get; }
    public string Age { get; }
    
    public Foo(string name, int age) => (Name, Age) = (name, age);
}
//with block body
public class Foo
{
    public string Name { get; }
    public string Age { get; }
    
    public Foo(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

Local Function

As of C# 7, you can declare functions inside other methods.
The inner Local function is only avaliable to the outer method.

Example of separating validation code from implementation code:

public static IEnumerable<char> AlphabetSubset(char start, char end)
{
    if (start < 'a' || start > 'z')
        throw new ArgumentOutOfRangeException(paramName: nameof(start), message: "start must be a letter");
    if (end < 'a' || end > 'z')
        throw new ArgumentOutOfRangeException(paramName: nameof(end), message: "end must be a letter");    
    if (end <= start)
        throw new ArgumentException($"{nameof(end)} must be greater than {nameof(start)}");
    
    return alphabetSubsetImplementation();
    
    IEnumerable<char> alphabetSubsetImplementation()
    {
        for (var c = start; c < end; c++)
            yield return c;
    }
}

Example of ensuring that exceptions from validation are thrown before asynchronous work begins:

public Task<string> PerformLongRunningWork(string address, int index, string name)
{
    if (string.IsNullOrWhiteSpace(address))
        throw new ArgumentException(message: "An address is required", paramName: nameof(address));
    if (index < 0)
        throw new ArgumentOutOfRangeException(paramName: nameof(index), message: "The index must be non-negative");
    if (string.IsNullOrWhiteSpace(name))
        throw new ArgumentException(message: "You must supply a name", paramName: nameof(name));
    
    return longRunningWorkImplementation();
    
    async Task<string> longRunningWorkImplementation()
    {
        var interimResult = await FirstWork(address);
        var secondResult = await SecondStep(index, name);
        return $"The results are {interimResult} and {secondResult}. Enjoy.";
    }
}

See Lambda Expressions for similar functionality.
    
Event

publisher classes raise events to alert subscriber classes
    
Operator

such as + ++ - * / ?: new

Indexer

aka Smart Array

allows a class to be indexed like an array
resembles a Property

public int this[int i]
{
    get;
    set;
}

Indexes can accept a variety of parameters. You can overload indexers by using different parameter lists in the same object.


public class MyClass<T>
{
    private List<T> list = new List<T>();
    private Dicationary<string, T> dictionary = new Dicationary<string, T>();
    
    public T this[int key]
    {
        get 
        {
            return list[key];
        }
    }
    
    public T this[int row, int column]
    {
        get 
        {
            return list[row][column];
        }
    }
    
    public T this[string key]
    {
        get
        {
            return dictionary[key];
        }
    }
    
    public T this[params string[] keys]
    {
        get
        {
            return //something that uses multiple string keys
        }
    }
}
//calling the indexors
MyClass<Object> myClass = new MyClass<Object>();
Object a = myClass[5];
Object b = myClass["name"];
Object c = myClass["a", "b", "c"];
    
Constructor

special functions called on class instantiation
Constructors do not have a return type

Customer() { }

Constructor chaining to a base-type constructor:

class A
{
    public A(int x) 
    {
        //handle x
    }
}
class B : A
{
    public B(int x, int y) : base(x)
    {
        //handle y
    }
}

Constructor chaining to another constructor in this type:

class A
{
    public A(int x) : this(x, defaultY)
    {
    }
    public A(int x, int y)
    {
        //handle x and y
    }
}

As of C# 7, constructors can be expression bodied:

class A
{
    public int X { get; set; }
    public int Y { get; set; }
    
    public A(int x, int y) => (X, Y) = (x, y);
}

Primary Constructor

(.Net 12)

Any parameters in the primary constructor are automatically in-scope for the whole type.
A primary constructor indicates that these parameters are necessary for any instance of the type.

(class, record class) The implicit parameterless constructor isn't emitted when a primary constructor is present.
(struct, record struct) The implicit parameterless constructor is always emitted, and always initializes all fields, including primary constructor parameters, to the 0-bit pattern.

If there is a primary constructor, its parameter list is defined in the class declaration line.
Primary constructors are synthesized from this syntax.

public class NamedItem(string name)
{
    public string Name => name;
}

Any explicitly written constructor must use the "this(...)" initializer syntax to invoke the primary constructor. Even an explicit parameterless constructor must invoke the primary constructor.

Calling a parent class's primary constructor

public class Widget(string name, int count) : NamedItem(name)
{
    public Widget() : this("N/A", 1) {}

    public int Count => count;
}

Add attributes to the primary constructor

[method: MyAttribute]
public class MyClass(string name)
{
}

Add attribute to a positional parameter.
Add xml comments to a positional parameter.

/// <summary>
/// Person record type
/// </summary>
/// <param name="FirstName">First Name</param>
/// <param name="LastName">Last Name</param>
public record Person(
    [property: JsonPropertyName("firstName")] string FirstName, 
    [property: JsonPropertyName("lastName")] string LastName
    );

If you don't want to use the auto-generated Property based on the Positional Parameter,
then you can define your own Property that matches the naming convention,
and is initialized to the Positional Parameter value.

public record Person(string FirstName, string LastName, string Id)
{
    internal string Id { get; init; } = Id;
}

Static Constructor

Special function called only once per type, either automatically before the first instance is created, or automatically before the first static method is called. The static constructor can initialize static fields, or run other one-time operations.

class A
{
    static A(int x)
    {
    }
    public A(int y)
    {
    }
}

Destructor / Finalizer

a special function to destruct a class
only one allowed per class
Destructors cannot be called, they are invoked automatically
the Destructor implicitly calls Finalize on the object's base class
Destructors do not have a return type

~Customer() { }

As of C# 7, destructors can be expression bodied:

~Customer() => Console.WriteLine("Finalized");

Nested Type

a class or struct declared inside another class
the inner class can only be used by the outer class

Deconstructor

C# 7 and above.

This pattern provides Type-to-Tuple conversion.

You can define multiple Deconstruct methods for a Type.
The deconstructor used will depend on the tuple it is being assigned to.


public class Person
{
    public int Age { get; set; }
    public string Name { get; set; }
    public string Title { get; set; }
    
    //constructor
    public Person(int age, string name, string title)
    {
        Age = age;
        Name = name;
        Title = title;
    }
    
    //deconstructor
    public void Deconstruct(out int age, out string name)
    {
        age = Age;
        name = Name;
    }
    
    //deconstructor
    public void Deconstruct(out int age, out string name, out string title)
    {
        age = Age;
        name = Name;
        title = Title;
    }
}

var person = new Person(23, "Smith", "Engineer");
var (age1, name1) = person;
var (age2, name2, title2) = person;

Deconstrutors can be added as Extension Methods.

Deconstructor as expression:

    public void Deconstruct(out int age, out string name, out string title) =>
        (age, name, title) = (Age, Name, Title);

Access Modifiers

Access modifiers can be used on members and types (classes, structs, interfaces).

Public

Accessible to the current assembly and any assembly referencing it

Private

Accessible to the same class or struct.
One instance of the class will have access to private members of another instance of the same class.

Protected

Accessible to the same class or struct, and to any class directly derived from this one.

Only accessible to other classes when referrenced through a derived type.

class A 
{ 
    protected int x = 5; 
}

class B : A 
{ 
    public B() 
    { 
        x = 6; //valid
    } 
}

class C
{
    public static void Main()
    {
        A a = new A();
        B b = new B();
        
        a.x = 10; //INVALID
        b.x = 10; //valid
    }
}

Internal

Accessible to the current assembly only

Protected Internal

Accessible to the current assembly, and to any class directly derived from this one in another assembly

Private Protected

(.Net 7.2)

Accessible to the current assembly, only by the containing class and derived classes

Other Modifiers

Abstract Class

A class that cannot be instantiated, it can only be derived from.

Classes are either abstract or concrete. (Concrete classes don't have a special keyword, they just lack the abstract keyword.)

Abstract Method

A method signature with no implementation that must be defined in the derived class
Only valid in abstract classes

Sealed Class

Class cannot be inherited from

New

Explicitly hides a member inherited from a base class, when that member was not virtual

Override

Defines a new implementation of an inherited virtual member

Virtual

Marks that a member may be overriden in a derived class

Static Member

A member that belongs to the type instead of to an instance of the type

Static Class

A class that cannot be instantiated, but whose static members can be accessed
All members of the class must also be static
The class is implicitly sealed

Const

immutable values known at compile time
only built-in types (primitives + Objects) can be declared as Constants
constants are implicitly also static
constant values can only be set when they are declared
    
Readonly

A field that can only be set (once) in a Constructor or where the field is declared
This field cannot be set in a derived class

Extern

A method that is implemented externally, such as in another assembly

Partial

Allows the definition of a class, struct, or interface to be spread across several files

Implicit

Declare an implicit user-defined type conversion.

class MyType
{
    private string term;
    private string description;
    
    public static implicit operator string(MyType a)
    {
        return a.ToString();
    }
    
    public override string ToString()
    {
        return term + ": " + description;
    }
}

Explicit

Declare an explicit user-defined type conversion. (You cannot define both an implicit and explicit conversion between the same two types)

class MyType
{
    private string term;
    private string description;
    
    public static explicit operator string(MyType a)
    {
        return a.ToString();
    }
    
    public override string ToString()
    {
        return term + ": " + description;
    }
}

Unsafe

Any block of code that uses pointers must be marked unsafe.

Unsafe code is unverifiable code.
Strong typing cannot be verified by the CLR when using pointer arithmetic.
It is the programmers job to ensure they do not introduce security risks or pointer errors.

Well written pointer operations can increase application performance.

The assembly will need to be compiled with the "-unsafe" option.

You can use unsafe on a Type declaration or a Member.

Marking a method:

unsafe static void MyMethod()
{
}

Marking a block of code:

unsafe
{
}

Record

See "Record" section above.

Required

(.Net 11)

required can modify a Field or Property
required members must be initialized when the object is initialized
- they can be initialized to Null if Null is an allowed value for that member

required cannot be applied to Interface members
but it can be used in class, struct, record class, and record struct

required members must be at least as visible as their containing type

derived types that override a required member must keep it required

Derived types can add the required modifier when overriding a property.
- This surprises me because it would break the Liskov Substitution principle - a derived type could require more properties be initialized than the base type...but if the base type has constructors with fewer params than the derived type then the derived type would already have default initialization for the extra fields in place...so ok this should be ok

A type with any required members may not be used as a type argument when the type parameter includes the "new()" constraint. The compiler can't enforce that all required members are initialized in the generic code.

Directives

You cannot intersperse directives within Xml comments - you'll either get a "badly formatted Xml" warning or an "Xml comment is not placed on a valid language element" warning.

Using

The using directive imports namespaces into your program. They do not have to be placed at the top of a file, but that is the standard.


using System;
using System.IO;

You can alias a type. This is useful if two Type names conflict.

using System;
using MyType = MyNamespace.MoreNamespace.AnotherNamespace.SomeType;
...
MyType.AddInts(5, 7);

You can alias a Generic Type. This is useful for really long Generic definitions.

using Widget = Company.Server.Mediator.UseCase.Whatsit<Company.Server.Models.SpecificUseCaseErrorType, Company.Server.Models.ThirdParty.Gizmo[]>;
...
Widget x = new Widget();

(.Net 4.6)

You can now import a specific static class.


//previously
using System;
using MyNamespace;
...
Utility.AddInts(5, 7);

//also previously
using System;
using static MyNamespace.Utility;
...
AddInts(5, 7);

//now
using System;
using MyNamespace.Utility;
...
AddInts(5, 7);

C# 10 (.Net 6) Global Usings

global using System;
This applies to the entire project.

C# 10 (.Net 6) Implicit Usings

    <PropertyGroup>
        <!-- Other properties like OutputType and TargetFramework -->
        <ImplicitUsings>enable</ImplicitUsings>
    </PropertyGroup>
    <ItemGroup>
        <!-- Remove a specific using that was added implicitly -->
        <Using Remove="System.Threading.Tasks" />
        <!-- Another way to add a global using -->
        <Using Include="System.Threading.Tasks" />
    </ItemGroup>
Implicitly add common global using directives to the project.

(C# 12) You can alias any Type

//alias a tuple
using Point = (int x, int y);

If Endif

A code block is only compiled if the specified symbol is (or is not) defined.

Test symbol:

    #if DEBUG
        //code
    #elif X
        //code
    #else
        //code
    #endif

Define and undefine symbols:

    #define X
    //code
    #undef X
    
WARNING

Warning will generate a level 1 warning with the specified message when you build.


    #WARNING sql stmt altered to allow build, do not deploy as-is

DEFINE

You can define your own preprocessor symbols. These symbols will only exist within the file they are defined in.


    #define TRACE
    #if TRACE
    //do something
    #endif
    #if (TRACE && DEBUG)
    //do something
    #endif

In Visual Studio, you can have symbols defined based on the compile mode.
    Project > Properties > Build > check Define DEBUG Constant

You can undefine symbols:

    #undef TRACE
    
Preprocessor Symbols

"DEBUG" is a preprocessor symbol.
The custom symbols you create with #define are preprocessor symbols.

Preprocessor symbols are either defined, or undefined. They are not set to a value like a variable.

You can also define symbols in the *.csproj file.

In *.csproj:

  <Project>
    ...
    <PropertyGroup>
      <DefineConstants>$(DefineConstants);MY_SYMBOL</DefineConstants>
    </PropertyGroup>
  </Project>
In code:

    #if MY_SYMBOL
    //do something
    #endif

"DefineConstants" overwrites all current constants.
To add your constant to the current constants, use the "$(DefineConstants);" clause.

You can view your custom defined constants in:
Project properties > Build tab > select a configuration > custom constants are listed at the top

The following are supposed to be built-in preprocessor symbols that are defined based on your target framework, but they aren't working for me.
[Target Framework Symbols]
Framework Moniker Symbol
---------------------------------------------
.NET Framework 2.0 --> net20 --> NET20
.NET Framework 3.0 --> net30
.NET Framework 3.5 --> net35 --> NET35
.NET Framework 4.0 --> net40 --> NET40
.NET Framework 4.5 --> net45 --> NET45
.NET Framework 4.5.1 --> net451 --> NET451
.NET Framework 4.5.2 --> net452 --> NET452
.NET Framework 4.6 --> net46 --> NET46
.NET Framework 4.6.1 --> net461 --> NET461
.NET Framework 4.6.2 --> net462 --> NET462
.NET Framework 4.7 --> net47 --> NET47
.NET Framework 4.71 --> --> NET471
.NET Framework 4.72 --> --> NET472

    #if NET40
    using System.Net;
    #else
    using System.Net.Http;
    using System.Threading.Tasks;
    #endif
Variance

Assignment Compatibility

Two types are assignment compatible if the right-hand-side belongs to a type that is the same as or is derived from the left-hand-side.

string s = "text";
object o = s;

public class Dog { }
public class Labrador : Dog { }

Labrador l = new Labrador();
Dog d = l;

No data is lost from the right-hand-side object. It maintains its original type.

Covariance

Covariance is a subset of assignment compatibility where the right-hand-side type defines a generic argument that is the same as or is derived from the left-hand-side's generic argument.

List<string> s = new List<string>() { "a", "b", "c" };
List<object> o = s;

object[] o = new string[] { "a", "b", "c" };

Example: the delegate specifies an object return type. You may assign a method to it that returns a string.

static string GetString() {...}
...
Func<object> myDelegate = GetString;

Declaring an interface generic type covariant:
(not valid for parameter types)
(cannot be used to define generic-type constraints)

interface ICovariant<out T>
{
    T GetThing();
    void DoThing(Action<T> callback);
}
This allows the interface method to have more-derived return types that the specified generic type.

Covariance operations may not be type-safe. (You may cause run-time errors)

Contravariance

Contravariance is a reversal of assignment compatibility.
The left-hand-side type defines a generic argument that is the same as or is derived from the right-hand-side's generic argument.

List<object> o = new List<object>() { "a", "b", "c" };
List<string> s = o;

//given method "static void SetObject(object o)"
Action<object> o = SetObject;
Action<string> s = o;

Example: the delegate specifies a string parameter type. You may assign a method to it that accepts an object.

static void SetObject(object o) {...}
...
Action<string> myDelegate = SetObject;

Declaring an interface generic type contravariant:
(not valid for return types)

interface IContravariant<in T>
{
    void SetThing(T t);
    void DoThing<T>() where T : U;
}
This allows the interface method to have more-derived argument types that the specified generic type.

Covariance operations may not be type-safe. (You may cause run-time errors)

Operators

Logical

&& || !

Short-Circuit Evaluation: Logical expressions are short-circuited, meaning evaluation will stop as soon as it has a definitive answer.

For example, this expression will not throw a null reference exception because evaluation ends before that error would be triggered:

Object x = null;
bool y = false;
if(y && x.Property) {
  //do something
}

Note that expressions passed to libraries do not always short-circuit. I've just found the IMongoQueryable from the MongoDb library does not short-circuit, likely because the entire expression is being converted into a different query language.

Prefix And Postfix Incrementors


int x = 5;
x++; //postfix x = x + 1
++x; //prefix x = x + 1
x--; //postfix x = x - 1
--x; //prefix x = x - 1

A prefix expression will return the new value of x.
A postfix expression will return the previous value of x.


int[] numbers = new int[] { 0,1,2,3,4,5 };
int index = 2;
int a = numbers[index++]; //a = numbers[2]
index = 2;
int b = numbers[++index]; //b = numbers[3]

In a for loop like (int x=0; x<length; x++) it does not make a difference.

Conditional Operator

Aka Ternary Operator

The Conditional Operator is used to write a Conditional Expression.

A single-line if/else statement:

bool result = (1 < 2) ? true : false;

(.Net 7.2)

Can produce a ref result.

ref var r = ref (array != null ? ref array[0] : ref otherArray[0]);

Null Coalesce (??)

aka null-coalescing


x = y ?? 0;
//if y is null, 0 is set instead


x = y ?? z ?? 0;
//chain it

Null-Coalescing Assignment (??=)

(in C# 8)

Only updates the variable value if it is currently null

widget.field ??= "smith";
// is the same as
if(widget.field == nul;)
{
    widget.field = "smith";
}

Null Conditional (?)

As of C# 6.0 (.Net Framework 4.6)

aka Null Propagator

Offers succinct null checking


return student?.GetGrades();
//if student is null, null is returned
//otherwise, GetGrades() is run


return students?[index];
//if students is null, null is returned
//otherwise, students[index] is returned


MyEvent?.Invoke(this, new EventArgs());
//if event has no listeners, do nothing
//otherwise, invoke the event

Null Forgiving (!)

As of C# 8.0

Declares to the compiler that a reference type value is definitely not null, so that you aren't given warnings about it.

In this example, "p" is a possibly-null value. The call to "Name" specifies that "p" is for-sure not null by that time.

Person? p = Find("John");
if (IsValid(p))
{
    Console.WriteLine($"Found {p!.Name}");

    Console.WriteLine($"Found {p.Value.Name}"); //instead of this
}

Another option here is

Person? p = Find("John");
if (IsValid(p))
{
    Console.WriteLine($"Found {p.Name}");
}

public static bool IsValid([NotNullWhen(true)] Person? person) => person is not null && person.Name is not null;

Is

Return true if object is of the specified type:

if(myObject is MyType)
{
}

(C# 7) Return true if object is of the specified type, and cast is to the variable:

if(myObject is MyType myVariable)
{
    //use myVariable
}

Yield

"Yield" can be used instead of IEnumerable.

Auto-return each element in the collection one at a time:

yield return myCollection;
yield break; //end iteration

Method as iterator:

using System.Collections.Generic;

foreach(int i in Power(2, 8))
{
    Console.WriteLine(i);
}

public static IEnumerable<int> Power(int a, int b)
{
    int result = 1;
    for(int i = 0; i < b; i++)
    {
        result = result * a;
        yield return result;
    }
}

Property as iterator:

using System.Collections.Generic;

MyClass myClass = new MyClass();
foreach(int x in myClass.Numbers)
{
    Console.WriteLine(x);
}

public class MyClass
{
    public IEnumerable<int> Numbers
    {
        get
        {
            yield return 5;
            yield return 78;
            yield return 2;
            yield return 12;
        }
    }
}

Default

Default Operator "default"
Default Value Expression "default(type)"

The default value of reference-types is null:

Object x = default(Object); //x = null

The default value of value-types is something like 0:

int x = default(0); //x = 0

You can define your own default value on classes and structs.

As of C# 7.1, you can use just "default" instead of "default(type)" anywhere the compiler can infer the type. This is called the Default Literal Operator.

public Object MyMethod(List<int> list = default)
{
    int x = default; //x = 0
    return default; //returns null
}

Spread

The spread operator is ".."

Operator Overloading

You can overload any operator.


public static MyClass operator +(MyClass a, MyClass b)
{
    return new MyClass(a.X + b.X);
}

You cannot overload logical and (&&) or logical or (||), but you can overload true, false, bitwise and (&), and bitwise or (|).

C# 6.0 One-Liners
    
Auto Property Initializers

Succinct read-only property initialization.

As of C# 6.0 (.Net Framework 4.6)


public string Name { get; } = "Steven";

public DateTime Timestamp { get; } = new DateTime();

public int Id { get; } = NextId();

Expression Bodied Methods

Succinct one-line methods.

As of C# 6.0 (.Net Framework 4.6)


Public override string ToString() => string.Format("{0} {1}", X, Y);

Expression Bodied Properties

Succinct one-line read-only properties.

As of C# 6.0 (.Net Framework 4.6)


Public int Area => Width * Height;
Numerics

Int

32-bit integer.

Accepted integer literal formats:

var a = 13;
var b = 1_000_000; //1,000,000

Long

64-bit integer.

Float

32-bit floating point number. Can hold 7 significant digits.

Cannot accurately store all numbers within its precision range.

Double

64-bit floating point number - more precise than float. Can hold 15 to 16 significant digits.

Cannot accurately store all numbers within its precision range.

Decimal

128-bit floating point number - more precise than double. Can hold 28 to 29 significant digits. Comes with slower operations.

Decimal can accurately store any number within its precision range.

Binary

Binary literal

public const int Sixteen =   0b00010000;
public const int ThirtyTwo = 0b00100000;
public const int SixtyFour = 0b01000000;
"0b" prefix indicates a binary number.

Digit Separator

As of C# 7, you can insert the "_" digit seperator anywhere in a number literal to improve its legibility.


public const int Sixteen =   0b0001_0000; //base 2
public const long BillionsAndBillions = 100_000_000_000; //base 10
public const double AvogadroConstant = 6.022_140_857_747_474e23; //base 10 decimal places

(.Net 7.2)

Binary and hexadecimals can have the digit separator right after the prefix:

public const int Sixteen =   0b_0001_0000; //base 2

NaN

NaN: Not a Number


if(Double.IsNaN(x)
{
}

Infinity


if(Double.IsInfinity(x)) //either negative or positive
{
}
if(Double.IsNegativeInfinity(x))
{
}
if(Double.IsPositiveInfinity(x))
{
}

Random


using System;

var random = new Random();
var r = random.Next(min, max+1); //include min and max as options

Collection Expressions

(C# 12)

Collection Expressions are a terse syntax for collection initialization

int[] myArray = [1, 2, 3];
List<string> myList = ["a", "b", "c"];
Span<char> mySpan  = ['a', 'b', 'c'];
int[][] myJaggedArray = [[1, 2, 3], [4, 5, 6]];

int[] row0 = [1, 2, 3];
int[] row1 = [4, 5, 6];
int[][] myJaggedArray2 = [row0, row1];

The spread operator (..) in a collection expression replaces its argument with the elements from that collection.

int[] row0 = [1, 2, 3];
int[] row1 = [4, 5, 6];
int[] single = [.. row0, .. row1];
foreach (var element in single)
{
    Console.Write($"{element}, ");
} //outputs "1, 2, 3, 4, 5, 6"
Strings

Regular String Literals


"string"
"\tstring" //escape character interpreted as Tab

Escape characters:
single quote \'
double quote \"
backslash \\
?? \0 \u \U
octal notation \[0-9]+
hex notation \x[0-9a-fA-F]{1,4}
alarm sound \a
backspace \b
formfeed \f
new line \n
carriage return \r
horizontal tab \t
vertical tab \v

Verbatim String Literals


@"string"
@"\tstring" //the backslash and t are kept as they are
@"quotes""here" //the only escape sequence is two double quotes are interpreted as one

Verbatim string literals can span multiple lines.

Raw String Literals

(C# 11)
can contain arbitrary text, including whitespace, new lines, embedded quotes, and other special characters without requiring escape sequences

string longMessage = """
    Line 1.
        Line 2.
    "quoted line" and more
    """;
The endline after the opening """ and the endline before the ending """ are not included.
Whitespace to the left of the ending """ is removed from all lines.

Raw string literal combined with string interpolation

var location = $$"""
    You are at {{{Longitude}}, {{Latitude}}}
    """;
Multiple $ characters denote how many consecutive braces start and end the interpolation.

String Interning

All string literals in your code are compiled into one distinct list to be referenced at runtime, so creating a string literal within a loop will not result in 1000 strings being created - each iteration will reference the same immutable string.

You can add strings to the interning list at runtime. Use this if you expect to use this custom string frequently:

String.Intern(myCustomString);

Composite Formatting

Supported by String.Format, Console.WriteLine, StringBuilder.AppendFormat, TextWriter.WriteLine, Debug.WriteLine, etc.

Note that {0} indicates param index 0. It can be used repeatedly throughout the string, such as "{0} {1} {0} {0}".

Insert text into string based on the index. This is faster than string concatenation when combining four or more strings.

string s = String.Format("{0} is on the {1} under the {2}", noun1, noun2, noun3);

Format numbers

Console.WriteLine("${#,0.00}", amount); //show exactly 2 decimal places, show at least one whole digit, use comma as thousands separator

Format dates

Console.WriteLine("{0:MM-dd-yyyy}", DateTime.Now);

Align text

Console.WriteLine("{0,10}{1,20}{2,-5}", a, b, c); //number after comma gives width of column, negative number means left-align text

(I think this is new in C# 6.0, but haven't found confirmation or what it is called)

Using variable name instead of index:

Console.WriteLine("My name is {@name} and age is {@age}.", name, age);

String Formatting

Padding

string x = "abc".PadLeft(5); //add spaces to beginning of string until length equals 5

Hexadecimal

int num = 56;
string upperCaseHex = num.ToString("X");
string lowerCaseHex = num.ToString("x");
string upperCaseHex_2Digits = num.ToString("X2");
string lowerCaseHex_2Digits = num.ToString("x2");

upperCaseHex = String.Format("{0:X}", num);
num = Convert.ToInt32(upperCaseHex, 16);

Repeat a character x times

string s = new String('#', x);

Interpolated Strings

C# 6.0 and later.

Interpolated strings can contain interpolation expression, surrounded by curly braces {}.
When the string is resolved, each interpolation expression is replaced by its result (as a string).


$"Hello"
$"Hello, {name}. Today is {DateTime.Now.DayOfWeek}."

Formatting results:

$"The time is {DateTime.Now:HH:mm}."
The "HH:mm" is called the "format string component".

Aligning results:

$"Columns|{"Left", -7}|{"Right", 7}"
The integer is the minimum number of characters to show.
Positive means right-aligned in that space, negative means left-aligned.

Special characters:

$"{{braces}}" //equals "{braces}"

Enums

An enum is a distinct type consisting of a set of named constants. They can be declared at the namespace level, or in a class or struct.

Each constant is automatically assigned an integer value, starting from 0 and incrementing by 1.

There is no validation when a value is set. An integer enum will accept any integer value, not just the ones you defined. You have to call Enum.IsDefined to check that explicitly.


public enum Duration { Daily, Weekly, Monthly };

You can change the starting integer:

public enum Duration { Daily=5, Weekly, Monthly };

You can specify all the values:

public enum Duration { Daily=5, Weekly=3, Monthly=223 };

The default datatype is int, but you can also use byte, sbyte, short, ushort, int, uint, long, or ulong.

public enum Duration : byte { Daily, Weekly, Monthly };

Flags

Enums are frequently used for flags, when you intend to combine several options:

public enum MessageType { Error=1, Warning=2, Notice=4, Additional=8 };
...
MessageType selectedMessages = MessageType.Warning | MessageType.Notice;
...
private bool IsFlagSet(MessageType flags, MessageType includesValue)
{
    return ((flags & includesValue) == includesValue);
}
Note that the explicit values are powers of 2. This allows you to use bitwise operators to combine and check the flags.

Convert

Converting a string to the enum:

public enum Duration { Daily, Weekly, Monthly };
Duration e = (Duration)Enum.Parse(typeof(Duration), "Daily");

Specify custom string value of an enum

using System.Runtime.Serialization;

public enum MyEnum
{
    [EnumMember(Value = "String A")]
    A,
    [EnumMember(Value = "String B")]
    B
}
When this object is serialized, it will use the custom value.
This does not affect ToString().

Get the EnumMember string value:

using System;
using System.Runtime.Serialization;

public static string GetEnumMemberValue(this Enum enumValue)
{
    var type = enumValue.GetType();
    var fieldInfo = type.GetField(enumValue.ToString());
    var attribute = (EnumMemberAttribute[])(fieldInfo.GetCustomAttributes(typeof(EnumMemberAttribute), false));

    return (attribute.Length > 0)
        ? attribute[0].Value
        : null;
}


Array


int[] x = new int[10];
int[] y = new int[] { 1, 2, 3 };

x64 Platform: array size limited to 2GB

Indexing

(.Net 8)

Index from end operator: hat "^". Hat is a unary prefix operator.
Index ^1 is the last element in the array.

int[] numbers = new int[] { 0, 1, 2, 3, 4, 5 };
Console.WriteLine(numbers[1]); //outputs 1
Console.WriteLine(numbers[^1]); //outputs 5

Select a range from an array with range operator ".."

int[] a = new int[] { 0, 1, 2, 3, 4, 5 };
int[] b = a[1..3];
Console.WriteLine(b[0]); //outputs 1
Console.WriteLine(b[1]); //outputs 2
//that's it, b.Length == 2
The range specifies an inclusive-start and exclusive-end index.

Edge cases

[..] //selects entire array
[..4] //selects from beginning to this index
[4..] //selects from index to the end

Set a Range as a variable

int[] a = new int[] { 0, 1, 2, 3, 4, 5 };
Range r = 1..3;
int[] b = a[r];

Get a range of values

int[] b = Enumerable.Range(0, 100).ToArray(); //values 0 to 99

Multidimensional Arrays


int[,] x = new int[5,8]; //5 rows, 8 columns each
int[,,]y = new int[5,8,3]; //5 rows, 8 columns each, depth of 3 each

int[,] x = new int[,] {{1,2,3}, {4,5,6}}; //init a [2,3] array
int z = x[1,2]; //z = 6

To get length of a dimension:

int rowCount = myArray.GetLength(0);
int colCount = myArray.GetLength(1);
int depthCount = myArray.GetLength(2);
List{T}

x64 Platform: generic list size limited to 2GB
Limit can be overridden with <gcAllowVeryLargeObjects enabled="true" />

Lists use a doubling algorithm: if they need more space, the double of their current space will be allocated to them. You can specify how much space they start with when you initialize the list.

Tuple

C# 4 and above.

A tuple is a data structure containing an ordered sequence of values of (possibly) different types.
Tuples are used where you need a new Type, but don't want to define a new Type.

Tuples can hold up to 8 elements.
Tuples are immutable.
Tuples are references types.

Accessing


Tuple<int, string, string> person = new Tuple<int, string, string>(1, "Steve", "Smith");

var person = Tuple.Create(1, "Steve", "Smith");

Console.WriteLine(person.Item1); //outputs 1
Console.WriteLine(person.Item2); //outputs Steve

The 8th element is saved (by default) as a tuple.

var nums = Tuple.Create(1, 2, 3, 4, 5, 6, 7, 8);

Console.WriteLine(nums.Item7); //outputs 7
Console.WriteLine(nums.Rest); //outputs (8)
Console.WriteLine(nums.Rest.Item1); //outputs 8

You can nest tuples inside tuples to store more than 8 elements.
Recommendation: don't. Create a proper Type for this data.

Parameter

You can have tuple parameters.


public void MyMethod(int x, Tuple<int, string, string> y)
{
}

Return

You can return tuples.
This is a way to return multiple values without a specific Type, nor by using out, nor ref.


var x = MyMethod();

public Tuple<int, string, string> MyMethod()
{
    return Tuple.Create(1, "Steve", "Smith");
}

Deconstruct

This is called deconstructing a tuple.
It breaks a tuple's values into separate variables.


var (age, name) = MyMethod();

(int age, string name) = MyMethod();

int age;
string name;
(age, name) = MyMethod();

As of C# 7, you can ignore some tuple elements while destructuring by using the "discard" operator "_".

public Tuple<int, string, int, string, string> MyMethod()
{
    return (1, "TX", 23, "Engineer", "Smith");
}

var (_, _, age, _, name) = MyMethod();

As of C# 7.1, tuple element names will default to the names of variable arguments:

int age = 23;
string name = "Smith";
var person = (age, name);
Console.WriteLine(person.name); //outputs Smith

C# 7 ValueTuple

A ValueTuple is a value-type Tuple.

Support named members:

(int age, string name) person = (23, "Smith");

var person = (age: 23, name: "Smith");

Console.WriteLine(person.name); //outputs Smith
Unnamed members can still be accessed by ".Item1" etc.

Return:

static (int, string) MyMethod()
{
    return (23, "Smith");
}

var (age, name) = MyMethod();

Console.WriteLine(name); //outputs Smith

static (int age, string name) MyMethod()
{
    return (23, "Smith");
}

var person = MyMethod();

Console.WriteLine(name); //outputs Smith
Spans

C# 7.2 and above.

Span<T> is in the System namespace.
Span<T> is a value type.
An instance of Span<T> is stored as a contiguous piece of memory.

Span<T> is indexed just like an array.


//can cast from array to span
var arr = new byte[10];
Span<byte> bytes = arr;

//can slice a span
Span<byte> slicedBytes = bytes.Slice(start: 5, length: 2);

Specify the span should be allocated to the stack:

Span<byte> bytes = stackalloc byte[2];

Can be declared readonly
which helps when working with immutable strings without making new copies in memory

string str = "hello, world";
string worldString = str.Substring(startIndex: 7, length: 5); // Allocates
ReadOnlySpan<char> worldSpan = str.AsSpan().Slice(start: 7, length: 5); // No allocation
Assert.Equal('w', worldSpan[0]);

Parameters

Parameters are the things in the method signature between parentheses.

Params

You can specify one 'params' parameter, and it must be the last parameter in the list. A 'params' parameter is always a Collection<T>. When calling the method, you can include any number of type T arguments.


class MyClass
{
    void TestA(params int[] list) { ... }
    void TestB(int a, string b, params object[] list) { ... }
}
...
myClass.TestA(3, 7, 242, 65);
myClass.TestB(56, "text", "a", new Student(), 34.545);

Method overloading only compares method signatures, and the "params" keyword (and other constraints) are not considered. Therefore, this is not allowed because the methods have the same signature:

public void Add(params MyClass[] elements) //must use array
{
    this.elements.AddRange(elements);
}

public void Add(MyClass[] elements) //not allowed - same method name with same parameter types
{
    this.elements.AddRange(elements);
}

public void Add(List<MyClass> elements) //this is ok!
{
    this.elements.AddRange(elements);
}

Out

'out' can be used to cause a parameter to be passed by reference AND the parameter does not have to be initialized before passing it in. The variable MUST have its value set within the function.


public bool TryParse(string text, out int parsedValue)
{
    //attempt to convert text into an integer
    //if successful, set parsedValue to that integer and return true
    //else, set parsedValue to -1 and return false
}

public void DemoOutKeyword()
{
    int parsedValue;
    if(TryParse("644", out parsedValue))
    {
        Console.WriteLine("Parse successful: {0}", parsedValue);
    }
}

'out' cannot be used on async methods.

As of C# 7, you can declare the out argument inline with the method call:

public int? ToInt(string input)
{
    if(int.TryParse(input, out int result)
        return result;
    return null;
}

Ref

'ref' can be used to cause a parameter to be passed by reference. The variable must be initialized before passing it in. The variable's value does not have to be changed within the function.


public void ChangeValue(ref int x)
{
    x += 5;
}

public void DemoRefKeyword()
{
    int x = 10;
    Console.WriteLine("Before: {0}", x);
    ChangeValue(ref x);
    Console.WriteLine("After: {0}", x);
}

'ref' cannot be used on async methods.

As of C# 7, you can also explicitly return a reference.
This example finds an element in a matrix and returns a reference to the selected element:

public static ref int Find(int[,] matrix, Func<int, bool> predicate)
{
    for (int i = 0; i < matrix.GetLength(0); i++)
        for (int j = 0; j < matrix.GetLength(1); j++)
            if (predicate(matrix[i, j]))
                return ref matrix[i, j];
    throw new InvalidOperationException("Not found");
}

ref var item = ref MatrixSearch.Find(matrix, (val) => val == 42);
item = 24; //overwrites the contents of that cell in the matrix

var item = ref MatrixSearch.Find(matrix, (val) => val == 42);
item = 24; //just this copy of the value is modified, the matrix is not affected
"ref locals" cannot be used with async methods.

Ref Readonly

As of C# 7.2, you can explicitly return a readonly reference.

public static ref readonly Object MyMethod()
{
    return ref someObject;
}
The returned object cannot be edited by the caller.

As of C# 12, you can specify "ref readonly" parameters.
A "ref readonly" parameter is passed by reference but will not be edited inside the method
These have the same behavior as "in" parameters but allow you to update existing APIs that have "ref" parameters to be "ref readonly" without breaking the API contract.

In

As of C# 7.2, an "in" parameter is passed by reference but will not be edited inside the method.


public void MyMethod(in Person person)
{
    //do something
}

Overloading

'out' and 'ref' look the same to method overloading resoluation. A method with neither will be seen as different from a method with either.

In this example, the "A" methods are valid, the "B" methods are valid, but the "C" methods will cause a compilation error because they are seen as identical.


void A(int, string);
void A(out int, string);

void B(int, string);
void B(ref int, string);

void C(out int, string);
void C(ref int, string);

Boxing And Unboxing

Boxing is assigning a value type variable to a reference type variable. Ie, wrapping a value type inside a reference type.

Unboxing is assigning a reference type variable to a value type variable. Ie, unwrapping a value type that was inside a reference type.
Arguments

Arguments are passed into a method when it is invoked. They are the other side of parameters.

Named Arguments

You can name a specific argument to:
1) specify an argument out of order
2) improve legibility of the caller, like "what does that bool mean?"


public void MyMethod(int a, int b = 0, int c = 1)
{
}

MyMethod(5, c: 6);
Named arguments must come after all unnamed arguments.

(.Net 7.2)

You can now use named arguments before unnamed arguments, provided they are in their correct position.
Generics

Generic Class

Encapsulates operations that are not specific to a particular data type
Commonly used for collections

You can convert a concrete class to a generic class by replacing types with type parameters.

public class MyGeneric<T>
{
    public MyGeneric(T myParam)
    {
    }
}

To inherit from a generic class and specify the type:

public class MyGeneric<T> { }

public class MySpecific : MyGeneric<MyType> { }

Getting the Type of a generic:

using System.Reflection;

public class OneGeneric<T> { }
public class TwoGeneric<T,U> { }

//within method...
Type typeOne = typeof(OneGeneric<>);
Type typeTwo = typeof(TwoGeneric<,>);
TypeInfo typeInfoOne = typeOne.GetTypeInfo();
TypeInfo typeInfoTwo = typeTwo.GetTypeInfo();

Generic Method

A method specifying a generic type


static void Swap<T>(ref T a, ref T b)
{
    T temp = a;
    a = b;
    b = temp;
}

Generic Call

Calling a generic method


MyGeneric<int> myInts = new MyGeneric<int>();
Utilities.Swap<int>(ref x, ref y);

Type Parameter Constraints


class MyGeneric<T> where T:IComparable { }
Requires type T to implement interface IComparable


class MyGeneric<T> where T:BaseClass { }
Requires type T to derive from class BaseClass


class MyGeneric<T,U> where T:class where U:struct { }
Requires type T to be a class and type U to be a struct


class MyGeneric<T> where T:IComparable,new() { }
Requires type T to implement interface IComparable, and to implement a parameterless constructor


class MyGeneric<T,U> where T:U { }
Requires type T to be of the same class as U, or of a derived class of U


public void MyMethod<T,U>(int a, string b) where T:AbstractClass,new() { }
All the same constraints can be applied to a generic method. The constraints go between the parameter list and the method body.

(.Net 7.3)

Constraint by type System.Enum.
Constraint by type System.Delegate.
Constraint where type is unmanaged.

Example: Paired Strongly-Type Generic Derivatives

Example demonstrating using abstract generic classes to create a paired set of derived classes that return strong-typed values.

Here are the base abstract classes. They provide weakly typed data.

    /// <summary>
    /// A wrapper around the <see cref='Node'/> class.
    /// Provides extension operations that affect <see cref='Node'/>.
    /// </summary>
    public abstract class ANodeWrapper
    {
        /// <summary>
        /// Returns the wrapped <see cref='Node'/>.
        /// </summary>
        /// <exception cref='Exception'>Node is already set.</exception>
        public Node Node {
            get {
                return node;
            }
            set {
                if(node != null)
                    throw new Exception("Node is already set.");
                node = value;
            }
        }
        private Node node;

        /// <summary></summary>
        protected ALinkWrapper linkWrapper;

        /// <summary></summary>
        public ANodeWrapper()
        {
        }

        /// <summary></summary>
        public ANodeWrapper(Node node)
        {
            Node = node;
        }
    }

    /// <summary>
    /// A wrapper around the <see cref='Link'/> class.
    /// Provides extension operations that affect <see cref='Link'/>.
    /// </summary>
    public abstract class ALinkWrapper
    {
        /// <summary>
        /// Returns the wrapped <see cref='Link'/>.
        /// </summary>
        /// <exception cref='Exception'>Link is already set.</exception>
        public Link Link {
            get {
                return link;
            }
            set {
                //this is the one weak point - can't do type checking at this level
                if(link != null)
                    throw new Exception("Link is already set.");
                link = value;
            }
        }
        private Link link;

        /// <summary></summary>
        protected List<ANodeWrapper> childNodeWrappers = new List<ANodeWrapper>();

        /// <summary></summary>
        public ALinkWrapper()
        {
        }

        /// <summary></summary>
        public ALinkWrapper(Link link)
        {
            Link = link;
        }

        /// <summary>Add child node to end of list.</summary>
        public virtual void Add(ANodeWrapper childNodeWrapper)
        {
            childNodeWrappers.Add(childNodeWrapper);
        }
    }

Here are the mid-level generic classes, still abstract. They provide strongly-typed data.

    /// <summary>
    /// The purpose of <see cref='ANodeWrapper'/> and <see cref='ANodeWrapper{T}'/> is to provide a paired set of strongly-type subclasses.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class ANodeWrapper<T> : ANodeWrapper where T:ALinkWrapper
    {
        /// <summary></summary>
        public T LinkWrapper {
            get {
                return (T)linkWrapper;
            }
            set {
                if(linkWrapper != null)
                    throw new Exception("LinkWrapper is already set.");
                linkWrapper = value;
            }
        }

        /// <summary></summary>
        public ANodeWrapper()
        {
        }

        /// <summary></summary>
        public ANodeWrapper(Node node) : base(node)
        {
        }
    }

    /// <summary>
    /// The purpose of <see cref='ANodeWrapper'/> and <see cref='ANodeWrapper{T}'/> is to provide a paired set of strongly-type subclasses.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class ALinkWrapper<T> : ALinkWrapper where T:ANodeWrapper
    {
        /// <summary></summary>
        public T[] ChildNodeWrappers {
            get {
                return childNodeWrappers.Cast<T>().ToArray();
            }
        }

        /// <summary></summary>
        public ALinkWrapper()
        {
        }

        /// <summary></summary>
        public ALinkWrapper(Link link) : base(link)
        {
        }

        /// <summary>Add child node to end of list.</summary>
        public override void Add(ANodeWrapper childNodeWrapper)
        {
            if(!(childNodeWrapper is T))
                throw new ArgumentException("ChildNodeWrapper is not of type T:" + typeof(T));
            childNodeWrappers.Add(childNodeWrapper);
        }
    }

Here are the paired concrete classes:

    public class NodeWrapper : ANodeWrapper<LinkWrapper>
    {
        public NodeWrapper()
        {
        }

        public NodeWrapper(Node node) : base(node)
        {
        }
        
        //many more extensions operations would go here
    }

    public class LinkWrapper : ALinkWrapper<NodeWrapper>
    {
        public LinkWrapper()
        {
        }

        public LinkWrapper(Link link) : base(link)
        {
        }
        
        //many more extensions operations would go here
    }

Here's the business logic operating on the base abstract classes, but returning strongly-type concrete data.

    /// <summary>
    /// Returns the tree rooted at <paramref name='startingNode'/> recreated with each element wrapped.
    /// The wrapped elements are still interconnected.
    /// </summary>
    /// <typeparam name="T">Node Wrapper type</typeparam>
    /// <typeparam name="U">Link Wrapper type</typeparam>
    /// <param name="startingNode"></param>
    public static T Wrap<T,U>(Node startingNode) where T : ANodeWrapper,new() where U : ALinkWrapper,new()
    {
        T nodeWrapper = new T();
        nodeWrapper.Node = startingNode;

        if(startingNode.Link != null)
        {
            U linkWrapper = new U();
            linkWrapper.Link = startingNode.Link;
            foreach(Node childNode in startingNode.Link.ChildNodes)
            {
                T childNodeWrapper = Wrap<T,U>(childNode);
                linkWrapper.Add(childNodeWrapper);
            }
        }

        return nodeWrapper;
    }
Extension Methods

Extension methods allow you to "add" methods to existing types without creating a derived type. In actuality, you are creating static methods that take in the specified type as a parameter, but you are able to call these methods as if they were a part of that type.

It is recommended to use them sparingly.

Ex: LINQ query operators which extend IEnumerable


public static class MyExtensions
{
    public static int WordCount(this String s)
    {
        return s.Split(new char[] {' ','.','?'}, 
            StringSplitOptions.RemoveEmptyEntries
        ).Length;
    }
}
...
int count = "Multi word test".WordCount();

The first parameter of an extension method specifies the type it extends, and uses the "this" keyword.

Both the extension method, and the class it is a part of, must be static.

You can organize extension methods into any part of your project.

Because extension methods are not actually a part of their objects, they cannot access private or protected members of that object.

Because extension methods are not actually a part of their objects, they can be called on null values, provided the type of the null is explicit.

Extension methods cannot override class/interface methods, so make sure their signature is unique.

.Net 2.0 does not support extension methods, only .Net 3.0 and higher.

Data Annotations

Located in System.ComponentModel.DataAnnotations.

Attributes that can be added to class properties. You can add multiple types of annotations to a single property.

Validation Attributes


[StringLength(50)] //maximum string length
[Range(0.01,100.00)] //range of valid values
[Required(ErrorMessage="Provide phone number.")]
[RegularExpression(@"....")] //field must conform to a pattern
[Compare("Password")] //compare one field to another
[EnumDataType(typeof(MyEnum))] //verifies that value is specified in the enum

The ErrorMessage property is available on all these attributes.

Display Attributes


[Display(...)] //lots of optional properties
[DisplayName("Author")]
[DataType(DataType.EmailAddress)] //tells how to format a string
[Name(...)]
[ShortName(...)]
[Description(...)] //usually for a tooltip
[Prompt(...)] //usually a watermark in an input field
[GroupName(...)] //used to group fields
[Order(...)]
[AutoGenerateField(...)]
[AutoGenerateFilter(...)]
[DisplayColumn(...)] //apply to parent in foreign key relationship
[SortColumn(...)]
[DisplayFormat(DataFormatString="{0:C}")]
[DisplayFormat(ConvertEmptyStringToNull=true, NullDisplayText="Not Specified")]

Custom Attributes

Make a class inheriting from ValidationAttribute and override IsValid. Optionally add new constructors to take in extra parameters.


//defining the attribute
public class MinStringLengthAttribute : ValidAttribute
{
    public int Min { get; set; }
    public override bool IsValid(object obj)
    {
        return (((string)obj).Length >= Min);
    }    
}
//using the attribute
[MinStringLength(Min=5)]

Or use CustomValidationAttribute

//defining the attribute
public class MyClass
{
    public static ValidationResult MyValidationMethod(object obj)
    {
        bool success = //code
        if(success)
            return ValidationResult.Success;
        return new ValidationResult("error message");
    }
}
//using the attribute
[CustomValidation(typeof(MyClass), "MyValidationMethod")]

Delegates

A delegate type represents references to methods with specific parameter lists and return types.


public delegate void MyDelegateA(string a);

MyDelegateA a = new MyDelegateA(methodA);
a("test");

void methodA(string a)
{
    Console.WriteLine(a);
}

Delegates cannot be overloaded; they are differentiated by their name alone.

Action

An action is a delegate that does not return a value.
You can define an Action type parameter without explicitly defining a delegate for it.


Action<int, string> myAction = new Action<int, string>(Square);
myAction(4, "A"); //"4A"

static void Square(int i, string s)
{
    Console.WriteLine("{0}{1}", i, s);
}

Wrapper functions:

ErrorHandler(() => {
    throw new Exception("error");
});

public static void ErrorHandler(Action operation)
{
    ErrorHandler<int>(() => { operation(); return 0; });
}

public static T ErrorHandler<T>(Func<T> operation)
{
    try
    {
        return operation.Invoke();
    }
    catch
    {
        Console.WriteLine("Error handling");
        throw;
    }
}

Func

A func is a delegate that returns a value.
The last data type in the definition is the return type.
You can define a Func type parameter without explicitly defining a delegate for it.


Func<int, int> myFunc = new Func<int, int>(Square);
int x = myFunc(4); //16

static int Square(int i)
{
    return i * i;
}

Multicast

You can build up a list of Action delegates, provided they do not use an "out" parameter. When executing the delegate, all methods in the list are executed.


using System;
using System.Text;

delegate void MyFunc(int x);

class Test
{
    public static void Main()
    {
        MyFunc a = new MyFunc(A);
        MyFunc b = new MyFunc(B);
        MyFunc c = a + b; //add to list: a, b
        MyFunc d = c + a; //add to list: a, b, a
        MyFunc e = d - b; //remove from list: a, a
        MyFunc f = d - a; //remove from list: a, b
        c(1);
        d(2);
        e(3);
        f(4);
    }
    
    public static void A(int a)
    {
        Console.WriteLine("A: "+a);
    }
    
    public static void B(int b)
    {
        Console.WriteLine("B: "+b);
    }
}

Expressions

Apply Expression To Object

Given an expression that selects a object's field or property
And given the new value to set that field or property to
And given an object
Apply the expression to change the value on the object

public class UpdateField<T, U>
{
    public Expression<Func<T, U>> Field { get; set; }
    public TField Value { get; set; }

    public UpdateField(Expression<Func<T, U>> field, U value)
    {
        Field = field;
        Value = value;
    }

    public override void Apply(T target)
    {
        var memberSelectorExpression = Field.Body as MemberExpression;
        if (memberSelectorExpression != null)
        {
            var property = memberSelectorExpression.Member as PropertyInfo;
            if (property != null)
            {
                property.SetValue(target, Value, null);
            }
        }
    }
}

Lambdas

aka Lambda Expressions

Lambdas create anonymous functions (unnamed functions).
Any lambda expression can be converted to a delegate type.

"=>" is the Lambda Declaration Operator

Expression Lambda

//syntax: (input_parameters) => an_expression;
Func<int, int> square = x => x * x; //the "return" statement is implied
Console.WriteLine(square(5)); //"25"

Statement Lambda

//syntax: (input_parameters) => { multiple_statements }
Func<int, int> square = x => { return x * x; }
Console.WriteLine(square(5)); //"25"

Example of using lambdas in LINQ's method-based syntax

int[] numbers = { 2, 3, 4, 5 };
var squaredNumbers = numbers.Select(x => x * x);

the input parameter list requires parentheses () if there is more than one parameter

Func<int, int, int> sum = (x, y) => x + y;

if parameter types cannot be inferred, then be explicit

Func<int, int, int> sum = (int x, int y) => x + y;

(C# 12) You can give default values in a lambda parameter list

var increment = (int x, int i=1) => x + i;
Event

publisher classes raise events to alert subscriber classes


public delegate void EventHandlerB(object sender, EventArgsB e);

public class Publisher
{
    public event EventHandler EventA; //uses basic EventHandler, EventArgs objects
    
    public event EventHandlerB EventB;
    
    public Publisher() { ... }
    
    private void Something() 
    {
        TriggerEventA();
        TriggerEventB();
    }
    
    private void TriggerEventA()
    {
        if(EventA == null) return;
        EventA(this, new EventArgs());
    }
    
    private void TriggerEventB()
    {
        if(EventB == null) return;
        EventB(this, new EventArgsB());
    }
}

public class Subscriber
{
    public Subscriber(Publisher publisher)
    {
        publisher.EventA += new System.EventHandler(OnEventA);
        publisher.EventB += new Publisher.EventHandlerB(OnEventB);
    }
    
    public void OnEventA(object sender, EventArgs e)
    {
    }
    
    public void OnEventB(object sender, EventArgsB e)
    {
    ]
}

public class EventArgsB : EventArgs { ... }

Passing

Passing event to another control

private void passMouseWheelToParent(object sender, MouseEventArgs e)
{
    Control parent = (sender as Control).Parent;
    System.Reflection.MethodInfo onMouseWheel = parent.GetType().GetMethod("OnMouseWheel", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
    onMouseWheel.Invoke(parent, new object[] { e });
}

Keys

When you click Enter in a single-line TextBox, you get an error-ding-sound. This will suppress it.

private void textBox_KeyDown(object sender, KeyEventArgs e)
{
    if(e.KeyCode == Keys.Enter)
    {
        e.Handled = true;
        e.SuppressKeyPress = true; //stop the error-ding from sounding
    }
}

Naming Conventions

Event delegate:

//subscriber handles the event "EventRaised"
void OnEventRaised(object sender, EventArgs args)
{
}
When there are multiple events with the same "EventRaised" name being handled, I'll use the naming convention "OnObjectEventRaised" to differentiate them.

Custom event arguments:

class SpecificEventArgs : EventArgs
{
    public SpecificEventArgs(/*required data*/)
    {
        //store data locally
    }
}
Usually, EventArgs are immutable objects so that an event subscriber cannot change the value before another event subscriber handles it.

Declare event in class:

//declare type for each specific event handler
public delegate void SpecificEventHandler(object sender, SpecificEventArgs args);
public event SpecificEventHandler NounVerb;

//or
//declare event without requiring a new type for each event handler
public event EventHandler<SpecificEventArgs> NounVerb;
Events are declared as public fields in classes. The compiler automatically adds wrappers to the event field so that it can only be accessed in valid ways - to add/remove subscribers to/from the event.

Invoke the event:

private void MyMethod()
{
    //previously
    if(NounVerb != null) //if there are no subscribers, the event will be null
    {
        NounVerb.Invoke(this, new SpecificEventArgs(/*arguments*/);
    }
    
    //Framework 4.6 and above
    NounVerb?.Invoke(this, new SpecificEventArgs(/*arguments*/);
}
Pass special value "EventArgs.Empty" to indicate that no information is being passed to the event handler.

Microsoft's example does not have a method just for invoking each specific event. So they don't show a naming convention for that pattern.
My suggestion:

private void InvokeNounVerb()
{
    if(NounVerb == null) return;
    NounVerb.Invoke(this, new SpecificEventArgs(/*arguments*/);
}

Event handlers should never have a return value.
To return data, it must be set in the EventArgs. Example: marking an event as cancelled by setting a field in the EventArgs.

You can manage internal events explicitly:

internal event EventHandler<SearchDirectoryArgs> DirectoryChanged
{
    add { directoryChanged += value; }
    remove { directoryChanged -= value; }
}
private EventHandler<SearchDirectoryArgs> directoryChanged;
Pattern Matching

C# Pattern Matching is distinct from C# Regular Expressions.

"Pattern Matching allows you to implement method dispatch on properties other than the type of an object."
"These features represent the first tentative steps toward supporting programming paradigms where data and functionality live apart. As the industry moves toward more microservices and other cloud-based architectures, other language tools are needed."
"Consider Pattern Matching when your algorithms depend on a fact other than the runtime type of an object."

C# 7 Switch

Switch can now be used on any type, not just ints/enums/strings.

Pattern Matching switch statements can check for types and for properties based on that type.

Example:

switch(fruit)
{
    //Type Pattern with When Condition
    case Apple apple when apple.Breed == "Red Delicious":
        MakeDeliciousApplePie(apple);
        break;
    //Type Pattern
    case Apple apple:
        MakeApplePie(apple);
        break;
    //Null Pattern
    case null:
        throw new NullReferenceException();
    default:
        throw new InvalidOperationException();
}
That case statement says:

if(fruit is Apple && (fruit as Apple).Breed == "Red Delicious")
{
    Apple apple = (fruit as Apple);
    MakeDeliciousApplePie(apple);
}
else if(fruit is Apple)
{
    Apple apple = (fruit as Apple);
    MakeApplePie(apple);
} 
else if(fruit == null)
{
    throw new NullReferenceException();
}
else
{
    throw new InvalidOperationException();
}
It is a type check and a cast to a new variable in one statement.

When used with Pattern Matching, the order of the case statements matters.
Case statements are checked top to bottom.
The first matching case statement is run and the rest are ignored.
Therefore, put your most specific case statements at the top and more general ones at the bottom.

The old style of switch statements still work, and are called "Constant Patterns".

Recommendation: don't use Pattern Matching switch statements until you are sure Polymorphism will not serve your needs.

C# 7 Is

Example:

if(input is int count)
{
    sum += count
}
Is the same as:

if(input is int)
{
    int count = (input as int);
    sum += count;
}

C# 8 Switch Expression

Switch statements that simply return a value can be written as expressions.
Switch expressions must either return a value or throw an Exception.

Example:

public enum Rainbow { Red, Orange, Yellow, Green, Blue, Indigo, Violet }

public static RGBColor FromRainbow(Rainbow colorBand) =>
    colorBand switch
    {
        Rainbow.Red    => new RGBColor(0xFF, 0x00, 0x00),
        Rainbow.Orange => new RGBColor(0xFF, 0x7F, 0x00),
        Rainbow.Yellow => new RGBColor(0xFF, 0xFF, 0x00),
        Rainbow.Green  => new RGBColor(0x00, 0xFF, 0x00),
        Rainbow.Blue   => new RGBColor(0x00, 0x00, 0xFF),
        Rainbow.Indigo => new RGBColor(0x4B, 0x00, 0x82),
        Rainbow.Violet => new RGBColor(0x94, 0x00, 0xD3),
        _              => throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand)),
    };
Note that the variable is listed before the switch keyword.
Note that each case statement ends with a comma.
Note that underscore (_) is a wild card match, like asterisk (*) is in a regular expression. This statement is called a "discard".
Note that the first case statement (from top to bottom) that matches the variable will be used, and the rest will be ignored.

C# 8 Property Pattern

A property pattern lets you match based on a property of the switch variable.

Example:

public class Address
{
    public string State { get; set; }
}

public static decimal ComputeSalesTax(Address location, decimal salePrice) =>
    location switch
    {
        { State: "WA" } => salePrice * 0.06M,
        { State: "MN" } => salePrice * 0.75M,
        { State: "MI" } => salePrice * 0.05M,
        _ => 0M
    };

Recommendation: this particular example could be rewritten to use object-oriented principles instead of this new syntax.

public class State
{
    public string Abbreviation { get; set; }
    public decimal SalesTax { get; set; }
}

public class Location
{
    public State State { get; set; }
}

public static decimal ComputeSalesTax(Address location, decimal salePrice) =>
    salePrice * location.State.SalesTax;


if(record.Customer is { Employed:true }) {
    //if record.Customer.Employed == true
    //can be used for checking multiple properties at once
}

C# 8 Tuple Pattern

You can use tuples to pass multiple variables into one switch statement.

Example:

public static string RockPaperScissors(string first, string second)
    => (first, second) switch
    {
        ("rock", "paper") => "rock is covered by paper. Paper wins.",
        ("rock", "scissors") => "rock breaks scissors. Rock wins.",
        ("paper", "rock") => "paper covers rock. Paper wins.",
        ("paper", "scissors") => "paper is cut by scissors. Scissors wins.",
        ("scissors", "rock") => "scissors is broken by rock. Rock wins.",
        ("scissors", "paper") => "scissors cuts paper. Scissors wins.",
        (_, _) => "tie"
    };

C# 8 Positional Patterns

This requires that the Type has an accessible Deconstruct method that converts it into a Tuple.

Example:

public class Point
{
    public int X { get; set; }
    public int Y { get; set; }
    
    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }
    
    public void Deconstruct(out int x, out int y)
    {
        (x, y) = (X, Y);
    }
}

public enum Quadrant { Unknown, Origin, One, Two, Three, Four, OnSomeBorder }

static Quadrant GetQuadrant(Point point) => 
    point switch
    {
        (0, 0) => Quadrant.Origin,
        var (x, y) when x > 0 && y > 0 => Quadrant.One,
        var (x, y) when x < 0 && y > 0 => Quadrant.Two,
        var (x, y) when x < 0 && y < 0 => Quadrant.Three,
        var (x, y) when x > 0 && y < 0 => Quadrant.Four,
        var (_, _) => Quadrant.OnSomeBorder,
        _ => Quadrant.Unknown
    };
The Point deconstructor is used implicitly to pass a tuple into the Switch statement.
The rest is the same as the Tuple Pattern.

Dependency Injection

This is about using the .Net ServiceProvider class as a Dependency Injection container.

Registering Implementations


public interface IMyInterface {}

public class MyImplementation : IMyInterface {}

//setup
using Microsoft.Extensions.DependencyInjection;
public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IMyInterface, MyImplementation>();
}

//example usage: Web API
public class MyController : Controller
{
    //this argument is automatically provided by the CLR with the appropriate concrete type
    public MyController(IMyInterface myInterface)
    {
    }
}

Registering Multiple Implementations

Scenario: you have multiple implementations of an interface, and each should be used in different places in your code.

"They will be returned in the same order they are registered in when injected into calling code. Depending on your requirements, this may be useful and important."


public interface IMyInterface {}

public class AImplementation : IMyInterface {}

public class BImplementation : IMyInterface {}

//setup
using Microsoft.Extensions.DependencyInjection;
public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IMyInterface, AImplementation>();
    services.AddScoped<IMyInterface, BImplementation>();
}

//example usage: Web API
using System.Linq;
public class MyController : Controller
{
    //this argument is automatically provided by the CLR with the appropriate concrete type
    public MyController(IEnumerable<IMyInterface> myInterfaces)
    {
        BImplementation b = myInterfaces[1] as BImplementation;
        //or to be less fragile
        BImplementation b = myInterfaces.OfType<BImplementation>().First();
        //or even better, using a method on your IMyInterface
        IMyImplementation x = myInterfaces.First(CanHandle("this operation"));
    }
}
Code Contracts

see Design By Contract

Benefits

Improved testing: Code contracts provide static contract verification, runtime checking, and documentation generation.

Automatic testing tools: You can use code contracts to generate more meaningful unit tests by filtering out meaningless test arguments that do not satisfy preconditions.

Static verification: The static checker can decide whether there are any contract violations without running the program. It checks for implicit contracts, such as null dereferences and array bounds, and explicit contracts.

Reference documentation: The documentation generator augments existing XML documentation files with contract information. There are also style sheets that can be used with Sandcastle so that the generated documentation pages have contract sections.

Usage


using System.Diagnostics.Contracts;

Most methods in the contract class are conditionally compiled; that is, the compiler emits calls to these methods only when you define a special symbol, CONTRACTS_FULL, by using the #define directive. CONTRACTS_FULL lets you write contracts in your code without using #ifdef directives; you can produce different builds, some with contracts, and some without.

Pre Conditions

The run-time behavior of failed preconditions is determined by the runtime analyzer.

Preconditions specify state when a method is invoked. They are generally used to specify valid parameter values.

Preconditions are listed at the top of a method.

Example:

class Calculations
{
    public int Division(int numerator, int denominator)
    {
        Contract.Requires(denominator != 0, "denominator cannot be 0");
        Contract.Requires(numerator > denominator, "numerator should be greater than denominator");
        
        return numerator / denominator;
    }
}

Example:

class Validation
{
    public string GetCustomerPassword(string customerID)
    {
        //throw an Exception if this pre condition fails
        Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(customerID), "Customer ID cannot be Null");

        string password="AAA@1234";
        if (customerID != null)
        {
            return password;    
        }
        else
        {
            return null;
        }
    }
}

Post Conditions

Example:

class Validation
{
    public string GetCustomerPassword(string customerID)
    {
        //post condition that the method does not return null
        Contract.Ensures(Contract.Result<string>() != null);

        string password="AAA@1234";
        if (customerID != null)
        {
            return password;    
        }
        else
        {
            return "unknown";
        }
    }
}

Invariants

Set contracts on method variables.


Contract.Invariants...

Interfaces

You can define contracts on interfaces.

Example:

[ContractClass(typeof(ValidationContract))]
interface IValidation
{
    string CustomerID{get;set;}
    string Password{get;set;}
}

[ContractClassFor(typeof(IValidation))]
sealed class ValidationContract:IValidation
{
    string IValidation.CustomerID
    {
        [Pure] //this get-method does not change the state of the object
        get
        {
            return Contract.Result<string>();
        }
        set
        {
            Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(value), "Customer ID cannot be null!!");
        }
    }

    string IValidation.Password
    {
        [Pure] //this get-method does not change the state of the object
        get
        {
            return Contract.Result<string>();
        }
        set
        {
            Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(value), "Password cannot be null!!");
        }
    }
}

ContractAbbreviator

(.Net 4.5)


Attributes

Caller

Get calling method name with stack trace:

using System.Diagnostics;

public void MyMethod()
{
    StackTrace stackTrace = new StackTrace();
    string callingMethodName = stackTrace.GetFrame(1).GetMethod().Name;
}

(.Net 4.5)

Get calling method name with built-in reflection

Uses reflection to provide runtime information:

using System.Runtime.CompilerServices;

public void MyMethod(normalParam, [CallerMemberName] string name = "", [CallerLineNumber] int lineNumber = 0, [CallerFilePath] string path = "")
{
    Console.WriteLine("This method called by: " + name);
    Console.WriteLine("This method called from line: " + lineNumber);
    Console.WriteLine("This method called from file: " + path);
}
Settings

Application-level settings can only be edited during design, or in the <Application>.exe.config file between application sessions.

User-level settings can be edited and saved during run-time.


Color myColor = Properties.Settings.Default.myColor;
Properties.Settings.Default.myColor = Color.AliceBlue; 
Properties.Settings.Default.Save(); 
Tasks

When a Task object is instantiated, it is a "hot" Task, meaning it is already running. You do not need to await a Task to start it running.

Any method using "await" must be declared "async".


using System.Threading.Tasks;

public async Task<bool> IsValid()
{
    return await service.IsValid();
}

Run multiple tasks in parallel

var resultA = await service.A();
var resultB = await service.B();

var taskA = service.A();
var taskB = service.B();

await Task.WhenAll(taskA, taskB);
var resultA = taskA.Result;
var resultB = taskB.Result;

Enumerating

When you write

foreach(int x in list) { ... }
It is compiled to

IEnumerator y = list.GetEnumerator();
while(y.MoveNext())
{
    int x = (int)y.Current;
}

IEnumerable

Method signature IEnumerator GetEnumerator();

IEnumerator

Property Current returns the currently selected element.

Method signature bool MoveNext() returns true if there is an element available to get.

Method signature void Reset() starts you back at the beginning of the enumerable.

Confirmed: when the enumerator is first created, or is reset, the pointer is BEFORE the first element in the collection, thus you start by calling MoveNext().


Equality

To enable equality comparisons in a custom object:

/// <duplicate cref="Equals(object)" />
public static bool operator ==(DotNetQualifiedName a, DotNetQualifiedName b)
{
    if(object.ReferenceEquals(a, null) && object.ReferenceEquals(b, null))
        return true;
    if(object.ReferenceEquals(a, null) || object.ReferenceEquals(b, null))
        return false;
    return a.Equals(b);
}

/// <duplicate cref="Equals(object)" />
public static bool operator !=(DotNetQualifiedName a, DotNetQualifiedName b)
{
    if(object.ReferenceEquals(a, null) && object.ReferenceEquals(b, null))
        return false;
    if(object.ReferenceEquals(a, null) || object.ReferenceEquals(b, null))
        return true;
    return !a.Equals(b);
}

/// <summary>Names converted to strings must match exactly to be considered equal.</summary>
public override bool Equals(Object b)
{
    if(!(b is DotNetQualifiedName))
        return false;
    if(object.ReferenceEquals(this, null) && object.ReferenceEquals(b, null))
        return true;
    if(object.ReferenceEquals(this, null) || object.ReferenceEquals(b, null))
        return false;

    DotNetQualifiedName other = (b as DotNetQualifiedName);
    return (this.LocalName == other.LocalName && this.FullNamespace == other.FullNamespace);
}

/// <summary></summary>
public override int GetHashCode()
{
    if(FullNamespace == null) return LocalName.GetHashCode();
    return LocalName.GetHashCode() ^ FullNamespace.GetHashCode();
}

/// <summary>
/// Names are sorted alphabetically, per namespace, starting with the root.
/// </summary>
public int CompareTo(object b)
{
    if(!(b is DotNetQualifiedName))
        return -1;
    if(this.Equals(b))
        return 0;
    if(object.ReferenceEquals(this, null))
        return -1;
    if(object.ReferenceEquals(b, null))
        return 1;

    DotNetQualifiedName other = (b as DotNetQualifiedName);
    int namespaceComparer = this.FullNamespace.CompareTo(other.FullNamespace);
    if(namespaceComparer != 0)
        return namespaceComparer;
    return this.LocalName.CompareTo(other.LocalName);
}

To avoid infinite recursion, use this method of checking for null values:

//use this
if (object.ReferenceEquals(myObject, null))

//instead of this
if(myObject == null)
Comparable

Interface IComparable is required for LINQ comparisons.


public MyClass : IComparable
{
    public int CompareTo(object other)
    {
        if(!(other is MyClass))
            return -1; //return whatever makes the most sense
        if(this < (other as MyClass))
            return -1;
        if(this == (other as MyClass))
            return 0;
        return 1;
    }
}

Clonable

Serialization

(I haven't tested this method yet, but it looks useful)

Use serialization to clone an object, without having to write out all the clone logic for a deep or complicated object.

public static T Clone<T>(T source)
{
    if (!typeof(T).IsSerializable)
    {
        throw new ArgumentException("The type must be serializable.", nameof(source));
    }

    // Don't serialize a null object, simply return the default for that object
    if (Object.ReferenceEquals(source, null))
    {
        return default(T);
    }

    IFormatter formatter = new BinaryFormatter();
    Stream stream = new MemoryStream();
    using (stream)
    {
        formatter.Serialize(stream, source);
        stream.Seek(0, SeekOrigin.Begin);
        return (T)formatter.Deserialize(stream);
    }
}
This serializes the object, then deserializes it into a new instance.
Configuration

AppSettings

Add an App.Config file to the project. Add settings like this.

<configuration>
    <appSettings>
        <add key="keyA" value="valueA" />
        <add key="keyB" value="valueB" />
    </appSettings>
</configuration>

Access it like so.

using System.Configuration; //install package System.Configuration.ConfigurationManager

public class MyClass
{
    public string KeyA
    {
        get
        {
            return ConfigurationManager.AppSettings["KeyA"];
        }
    }
}

One way to have different settings for Debug vs Release:
1. Add to the project a configuration-specific file (*.csproj file)

  <ItemGroup>
    <None Include="App.config" />
    <None Include="App.Debug.config" />
    <None Include="App.Release.config" />
  </ItemGroup>
2. Add an event at compilation time to copy the right config file over "App.config" (*.csproj file)

  <!-- copy the correct config file into the correct location before compilation occurs -->
  <Target Name="SetAppConfig" BeforeTargets="Compile">
    <Copy SourceFiles="App.Debug.config" DestinationFiles="App.config" OverwriteReadOnlyFiles="true" Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' " />
    <Copy SourceFiles="App.Release.config" DestinationFiles="App.config" OverwriteReadOnlyFiles="true" Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " />
  </Target>

Add Reference

If you need to add a library reference in a config, do this:

<?xml version="1.0"?>
<configuration>
    <system.web> <!--Web.config example-->
        <compilation debug="true" targetFramework="4.5.2">
            <assemblies>
                <add assembly="System.ServiceModel.Activations"/><!--new reference-->
            </assemblies>
        </compilation>
    </system.web>
</configuration>
Logging

Microsoft.Extensions.Logging


using Microsoft.Extensions.Logging;

ILogger<MyClass> logger = new ILogger<MyClass>();

Message message = new Message("text", DateTime.Now);

logger.LogInformation($"Message: {message}"); //outputs Message: Namespace.Message
logger.LogInformation("Message: {@message}", message); //outputs Message: Message{"Text":"text","Timestamp":"YYYY-MM-DD..."}
Automapper

[automapper]

AutoMapper is a library (availably by NuGet package) that simplifies mapping one object to another.

Ex configuration:

using AutoMapper;

public class MyMapperProfile : Profile
{
    public MyMapperProfile()
    {
        //create a default mapping from A to B - mapping is determined by Property Names matching
        CreateMap<ObjectA, ObjectB>();

        //create a custom mapping from A to C
        //d for destination, s for source
        CreateMap<ObjectA, ObjectC>()
            .MapFrom(d => d.Name, s => s.Person.Name)
            .MapFrom(d => d.Type, s => s.Type)
            .ForMember(d => d.Contacts, o => o.MapFrom(s => GetContacts(s)))
            .ForMember(d => d.Addresses, o => o.MapFrom(s => s.Addresses))
            ;
        CreateMap<AddressA, AddressC>();
    }
    
    private Contact[] GetContacts(ObjectA a)
    {
        return new Contact[] {};
    }
}

Ex usage:

var mapper = new MyMapperProfile();
return mapper.Map<SomeType>(response);

Ex usage:

var config = new MapperConfiguration(cfg => {
    cfg.AddProfile<MyProfile>();
    cfg.CreateMap<ModalA, ModelB>();
});

Ex usage:

var config = new MapperConfiguration(cfg => {
    cfg.CreateMap<ModelA, ModelB>();
});
IMapper iMapper = config.CreateMapper();
var myModelB = iMapper.Map<ModelA, ModelB>(myModelA);

Pros

For 1-to-1 mappings where all the property names match perfectly, it saves you writing boiler plate code.

Cons

1)
There is no compiler error when you use a conversion that does not exist. The error only appears at runtime.


return mapper.Map<SomeType>(response); //no compilation error

2)
For complex mappings, you aren't saved any complexity in your implementation.

3)
It's another object to initialize and use.
I prefer using Extension Methods to enable fluent syntax, and this adds an extra layer on that.

Regular Expression Object

Regular expressions quickly parse large amounts of text to find specific character patterns. They extract, edit, replace, and delete text substrings.


using System.Text.RegularExpressions;

RegexStringValidator

If you are just validating a string conforms to a pattern, use a RegexStringValidator.


RegexStringValidator validator = new RegexStringValidator(@"^[A-Z]{3}$");
try
{
    validator.Validate(myString);
}
catch(ArgumentException)
{
    //validation failed
}

Validate is a void method, it just throws an exception if the comparison fails.

Specific Substring

If you are looking for a specific substring (rather than a pattern) use the String class methods.


myString.Contains(substring);
myString.StartsWith(substring);
myString.EndsWith(substring);
myString.IndexOf(substring);
myString.LastIndexOf(substring);

Static Vs Instance Methods

All the Regex methods have both a static version and an instance version.

Regex is an immutable object, so when using instance methods, it will compile your search pattern once and let you use it over and over.

If you use the static methods, your search pattern will be compiled and cached. Usually the cache holds up to 15 pattern, so performance is similar for either method, up to a point.

Examples


Regex r = new Regex(@"\b(\w+)\s\1\b", RegexOptions.IgnoreCase);
MatchCollection matches = r.Matches(myString);
foreach(Match match in matches)
{
    Console.WriteLine(match.Value);
}


if(r.IsMatch(myString))
{
    //at least one match was found
}


Match match = r.Match(myString); //returns first match
while(match.Success)
{
    Console.WriteLine("'{0}' found at index {1}", match.Value, match.Index);
    match = match.NextMatch();
}


string[] splits = r.Split(myString); //splits string at beginning of each match


//instance version
string result = r.Replace(myString, replacementString); //replace all matches with replacement

//static version
string result = Regex.Replace(myString, @"\b(\w+)\s\1\b", replacementString);


public MyClass
{
    public static string MyMethod(Match m)
    {
        //return replacement string for this particular match
    }
}
string result = r.Replace(myString, new MatchEvaluator(MyClass.MyMethod));

Capture groups:

Match match = Regex.Match(searchString, @"([a-z]+) ([a-z]+)", RegexOptions.IgnoreCase);
while(match.Success) //one match per "word word" found
{
    string value = match.Value;
    int index = match.Index;
    string wholeMatch = match.Groups[0].Value;
    string word1 = match.Groups[1].Value;
    string word2 = match.Groups[2].Value;
    
    match = match.NextMatch();
}

Regular Expression Patterns

Character Escapes

\a = \u0007 = alarm sound
\b = \u0008 = backspace
\t = \u0009 = tab
\n = \u000A = new line
\v = \u000B = vertical tab
\f = \u000C = form feed
\r = \u000D = carriage return
\e = \u001B = escape key
\nn or \nnn (where each n is a digit) = octal character (ex: \040 = space)
\xnn (where each n is a hex-digit) = hexadecimal character (ex: \x20 = space)
\cX or \cx (where X and x are alphabet characters) = control-character (ex: \cC = Ctrl-C)
\unnnn (where each n is a hex-digit) = unicode character (ex: \u0020 = space)
\x (where x is any character) = use the character as is (ex: \* = asterisk)

Anchors

Anchors are "atomic zero-width assertions", meaning they cause a match to succeed or fail based on the current position in the string, and they do not cause the current position to change.

^ = beginning of string or line
$ = end of string or line
\A = beginning of string
\Z = end of string or line ??
\z = end of string
\G = beginning of string, or end of previous match
\b = boundary between \w and \W (or vice versa)
\B = not \b


Pattern "\Gx\dx"
String "x1xx3xx5xy7yx9x"
Matches "x1x", "x3x", "x5x"

Character Classes

[x] = matches any single character inside braces (case sensitive by default)
[^x] = matches any single character NOT inside braces
[x-y] = matches any single character in the range from x to y (including x and y)
[x^y] = matches x, ^, or y (equivalent to [\^xy])


"[abg-mx]" = "[abghijklmx]"

. = wildcard, matches any single character (except "\n")
\p{name] = matches any single character in the named Unicode category or block
\P{name} = not \p{name}


\p{Lu} or \p{IsCyrillic}

\w = matches any single "word" character = alphabet and digits and underscore
\W = not \w
\s = matches any single whitespace character
\S = not \s
\d = matches any single digit
\D = not \d

Grouping Constructs

Grouping constructs delineate subexpressions and capture substrings.

(subexp) = captures subexp match and assigns it to an ordinal group (starting at 1)


Pattern "(\w)\1"
String "AaBBccdeed"
Matches "BB", "cc", "ee"

(?<name>subexp) = captures subexp match and assigns it to a named group


Pattern "(?<letter>\w)\k<letter>"
String "AaBBccdeed"
Matches "BB", "cc", "ee"

(?<nameA-nameB>subexp = creates a balancing group (see Balancing Group Definition below)

(?:subexp) = does not capture subexp match
(?imnsx-imnsx:subexp) = toggles match options within the subexp (see Miscellaneous Constructs)


"(?i:\w)" //turns ignore case on within the subexp
"(?-i:\w)" //turns ignore case off withint the subexp
"(?i-s:\w)" //turns ignore case on, and single line mode off

(?=subexp) = zero-width positive look ahead assertion, like an Anchor looking for a specific pattern


Pattern "\w+(?=\.)"
String "The dog. Is Happy."
Matches "dog", "Happy" because they have a period after them

(?!subexp) = zer-width negative look ahead assertion, like an Anchor looking for anything but this specific pattern

(?<=subexp) = zero-width positive look behind assertion, like an Anchor looking for a specific pattern


Pattern "(?<=19)\d{2}\b"
String "1851 1999 1950 1905 2003"
Matches "99", "50", "05" because they have a 19 before them

(?<!subexp) = zero-width negative look behind assertion, like an Anchor looking for anything but this specific pattern

(?>subexp) = greedy subexpression, meaning no backtracking

The greedy subexpression is used to speed up large processes. No backtracking means that if (ex) string index 5 through 9 seem to match but index 10 makes the match fail, then checking will continue forward from 10 instead of backtracking to 6.

Quantifiers

* = match previous element 0 or more times
+ = match previous element 1 or more times
? = match previous element 0 or 1 times


"x*" matches "", "x", "xx", "xxx"...
"(xyz)*" matches "", "xyz", "xyzxyz"...

{n} or {n}? = matches previous element exactly n times
{n,} = matches previous element at least n times
{n,m} = matches previous element from n to m times

You can append a ? to end of any quantifier to make it match as few times as possible.

*? = matches previous element 0 or more times AND ALSO as few times as possible
+? = matches previous element 1 or more times AND ALSO as few times as possible
?? = matches previous element 0 or 1 times AND ALSO as few times as possible
{n,}? = matches previous element at least n times AND ALSO as few times as possible
{n,m}? = matches previous element n to m times AND ALSO as few times as possible

Backreference Constructs

\number = matches the value of a numbered subexpression

These groups are numbered left to right, starting at 1, and can go above 9.


Pattern "(aaa)x\1"
String "aaaxaaa"
Matches "aaaxaaa"

\k<name> = matches the value of a named subexpression

These named groups are still given a number, in sequence with the non-named groups.


Pattern "(?<abc>ddd)x\k<abc>x\1"
String "dddxdddxddd"
Matches "dddxdddxddd"

Alternation Constructs

| = matches any 1 element separated by vertical bar


Pattern "th(e|is|at)"
String "the house is this way, not that"
Matches "the", "this", "that"

(?(subexp)yes|no) = the subexp is a zero-width assertion, if assertion is true then match yes otherwise match no


Pattern "(?(Wh)at|one)"
String "What is the tone?"
Matches "What", "one"

(?(name)yes|no) = name is the name or number of a backreference construct, if that construct got a match then match yes otherwise match no


Pattern "(xxx)(?(1)a|b)"
String "bxxxabb"
Matches "b" (the first one), "a"

Substitutions

These are language elements supported in replacement patterns.

$n = (where n is a number) uses substring captured by group n
${name} = uses substring captured by group "name"
$$ = literal $ character
$& = uses whole match
$` = (that is a backtic) uses all of input string up to the match
$' = (that is a quote) uses all of input string after the match
$+ = uses substring captured by last group
$_ = uses entire input string

Options

i = case insensitive
m = multiline mode (^ and $ match start and end of lines, instead of whole input)
n = do not capture unnamed groups
s = single line mode
x = ignore unescaped whitespace in the pattern (use for legibility)


(with x option) "\b(?x) \d+ \s \w+" = "\b(?x)\d+\s\w+"

Miscellaneous Constructs

(?imnsx-imnsx) = toggles the match option for everything to the right


Pattern "A(?i)b\w+"
String "ABC Able aBC"
Matches "ABC", "Able"

(?#comments) = comment your pattern
# = comment starts at unescaped # and goes to end of line

Balancing Group Definition

Matching recursive (nested) structures.

(?<nameA-nameB>subexp)
(?'nameA-nameB'subexp)
(?<-nameB>subexp)

Consider all the matches for nameB to be on a stack. When this match for nameA is found, one match from nameB is popped off the stack. All the text from the last match for nameB to this current match for nameA is pushed onto nameA's stack.

If regex attempts to pop from the nameB stack when the stack is empty, the match fails.

One use is to validate that nested open/close angle braces are valid:

string plainPattern = "^[^<>]*(((?'Open'<)[^<>]*)((?'Close-Open'>)[^<>]*)+)*(?(Open)(?!))$";

//ignore these line breaks, they were added for commenting
string explainedPattern = "
^[^<>]* //allow any number of non-brace characters at beginning
(
    ((?'Open'<)[^<>]*) //"<" pushed onto "Open" stack
    ((?'Close-Open'>)[^<>]*)+ //text pushed onto "Close" stack and one "<" popped from "Open" stack
)*
(?(Open)(?!))$ //success if "Open" stack is empty at the end of the input, because the (?!) is not executed
";

The example above uses (?!). This is a zero-width negative look ahead assertion that will always fail, because it checks for an empty string and you can always find an empty string. Thus this assertion always fails.

Graphics

Create Image


using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;

Bitmap bitmap = new Bitmap(width, height);
using(Graphics graphics = Graphics.FromImage(bitmap))
{
    graphics.SmoothingMode = SmoothingMode.AntiAlias;
    graphics.Clear(Color.White);
    //draw on graphics
}
bitmap.Save(saveAsFilename, ImageFormat.Png);

Open Image

To open and edit an image.

using System.Drawing;

public class MyClass
{
    public MyClass()
    {
        Image image = Image.FromFile(fullPath);
        using(Graphics graphics = Graphics.FromImage(image))
        {
            //edit image
        }
        image.Save(fullpath);
    }
}

Strings

Draw text:

using(Graphics graphics = Graphics.FromImage(image))
{
    string text = "Sample Text";
    System.Drawing.Font font = new System.Drawing.Font("Arial", 16);
    System.Drawing.SolidBrush brush = new System.Drawing.SolidBrush(System.Drawing.Color.Black);

    graphics.DrawString(text, font, brush, x, y);
}

Find out how much space the string will take up.

SizeF size = graphics.MeasureString(text, font);

Lines


graphics.DrawLine(pen, startPoint, endPoint);

Arcs

Arcs are bound by Rectangles, start at a degree, and go through a sweep-degree.


//fill in an arc
graphics.FillPie(new SolidBrush(Color.White), rectangle, 0, 360);
//outline an arc
graphics.DrawArc(new Pen(Color.Black, 1), rectangle, 0, 360);

Flip Image

Images use a top-left origin point, like computer screens do.

Flip graphics before drawing on it, so you can calculate using a bottom-left origin.

private void FlipTopToBottom(Graphics graphics, int height)
{
    graphics.ScaleTransform(1, -1);
    graphics.TranslateTransform(0, -height);
}

PDFSharp

Using the PDFSharp library to create PDF documents.

Basic

How to create a new document, draw text on it, and save it:

using System;
using PdfSharp;
using PdfSharp.Drawing; //for XFont
using PdfSharp.Drawing.Layout; //for XTextFormatter
using PdfSharp.Pdf; //for PdfDocument

class Program
{
    static void Main(string[] args)
    {
        PdfDocument document = new PdfDocument();
        PdfPage page = document.AddPage();

        XFont font = new XFont("Verdana", 20, XFontStyle.Bold);
        XGraphics graphics = XGraphics.FromPdfPage(page);
        graphics.DrawString("Testing Text", font, XBrushes.Black, new XRect(0, 0, page.Width, page.Height), XStringFormats.Center);
        
        document.Save("test.pdf"); //creates new or overwrites old
    }
}

Text

XGraphics.DrawString will not line wrap. Use XTextFormatter instead.

PdfPage page = document.AddPage();
XFont font = new XFont("Times New Roman", 12, XFontStyle.Regular);
string text = "The quick brown fox jumped over the lazy dog in the house that Jack built with his two good hands.";
XRect rectangle = new XRect(0, 0, page.Width, page.Height);
using(XGraphics graphics = XGraphics.FromPdfPage(page))
{
    XTextFormatter textFormatter = new XTextFormatter(graphics);
    textFormatter.DrawString(text, font, XBrushes.Black, rectangle, XStringFormats.TopLeft);
}

MeasureString:

using(XGraphics graphics = XGraphics.FromPdfPage(page))
{
    XSize measure = graphics.MeasureString(text, font);
    //measure.Width
    //measure.Height
}
MeasureString ignores leading and trailing spaces.

There is no built in measure for how tall a line-wrapped XTextFormatter string is. Here is a home-rolled solution.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PdfSharp.Drawing;

namespace PdfSharpExtensions
{
    public static class XGraphicsExtension
    {
        public static string[] BreakParagraphIntoLines(this XGraphics graphics, string text, XFont font, double width)
        {
            List<string> lines = new List<string>();
            int startIndex = 0;
            int endIndex = 0;
            int lastSpaceIndex = -1;
            string substring = "";
            while(endIndex < text.Length - 1)
            {
                if(text[endIndex] == ' ')
                {
                    lastSpaceIndex = endIndex;
                }

                substring = text.Substring(startIndex, endIndex - startIndex + 1);
                XSize measure = graphics.MeasureString(substring, font);
                if(measure.Width > width)
                {
                    substring = text.Substring(startIndex, lastSpaceIndex - startIndex + 1);
                    lines.Add(substring);
                    startIndex = lastSpaceIndex + 1;
                    endIndex = startIndex;
                }
                else
                {
                    endIndex++;
                }
            }
            substring = text.Substring(startIndex);
            if(!lines.Contains(substring))
            {
                lines.Add(substring);
            }
            return lines.ToArray();
        }

        public static double MeasureStringHeightWithLineWrap(this XGraphics graphics, string text, XFont font, double width)
        {
            double height = graphics.MeasureString(text, font).Height;
            string[] lines = graphics.BreakParagraphIntoLines(text, font, width);
            return (double)lines.Length * height;
        }
    }
}

Fonts

There are two ways to embed fonts in your pdf.

Specify each font to be embedded:

XPdfFontOptions options = new XPdfFontOptions(PdfFontEmbedding.Always);
XFont font = new XFont("Times New Roman", 12, XFontStyle.Regular, options);

Automatically embed all fonts used on a particular page:

PdfPage page = new PdfPage();
XGraphics graphics = XGraphics.FromPdfPage(page);
graphics.MFEH = PdfFontEmbedding.Automatic;

Units

1 XUnit = 1/72 Inches

Conversions

PdfPage page = document.AddPage();
page.Width = XUnit.FromInch(8.5);
page.Height = XUnit.FromInch(11);
page.Orientation = PageOrientation.Portrait;

Drawing

Colors

XColor paleGray = new XColor { R = 229, G = 229, B = 229 };
XPen outline = new XPen(paleGray);
XBrush fill = new XSolidBrush(paleGray);
graphics.DrawRectangle(new XPen(paleGray), new XSolidBrush(paleGray), XRect);
//or
graphics.DrawRectangle(XPens.LightGray, XBrushes.LightGray, XRect);

Lines

graphics.DrawLine(XPens.Black, x1, y1, x2, y2);
//or
XPen pen = new XPen(XColors.Black, lineThickness);
graphics.DrawLine(pen, x1, y1, x2, y2);

Rectangles

//outline
graphics.DrawRectangle(XPens.Black, x, y, width, height);
graphics.DrawRectangle(XPens.Black, XRect);
//filled
graphics.DrawRectangle(XPens.Black, XBrushes.LightGray, x, y, width, height);
graphics.DrawRectangle(XPens.Black, XBrushes.LightGray, XRect);

Circles

graphics.DrawArc(linePen, x, y, width, height, startDegrees, sweepDegrees);
The circle fits within the bounding rectangle.
0 start = center-right
Positive sweep moves clockwise, negative sweep moves counter-clockwise.

Pie Segments

//outline
graphics.DrawPie(linePen, x, y, width, height, startDegrees, sweepDegrees);
//filled
graphics.DrawPie(XBrushes.Black, x, y, width, height, startDegrees, sweepDegrees);

Images

XImage ximage = XImage.FromFile(fullPathFilename);
graphics.DrawImage(ximage, x, y, ximage.PointWidth, ximage.PointHeight);
CreateSpace recommends using images with at least 200dpi resolution to avoid blurring on PDFs.

Rotating graphics

//put the rotated section in its own using/dispose code block
using(XGraphics graphics = XGraphics.FromPdfPage(page))
{
    graphics.RotateAtTransform(90, new XPoint(page.Width / 2, page.Height / 2));
    graphics.DrawLine(linePen, 0, 0, 300, 300);
}
Or

//save and restore state
XGraphicsState state = graphics.Save();
graphics.RotateAtTransform(90, new XPoint(page.Width / 2, page.Height / 2));
graphics.DrawLine(linePen, 0, 0, 300, 300);
graphics.Restore(state);
Positive degrees rotates the entire graphics section clockwise around the specified point.
If you rotate 90 degrees (clockwise), then a negative Y (moves object left/right) and positive X (moves object up/down) will bring drawings into view.

Working example of vertical text, with the bottom of the text facing the left:

using(XGraphics graphics = XGraphics.FromPdfPage(page))
{
    graphics.RotateAtTransform(90, new XPoint(0, 0));
    XRect rotatedRectangle = new XRect(
        originalRectangle.Y,
        0 - originalRectangle.X - originalRectangle.Width,
        originalRectangle.Height,
        originalRectangle.Width
    );
    graphics.DrawString("Vertical Text", font, XBrushes.Black, rotatedRectangle, XStringFormats.TopLeft);
}

Rotating at the (0,0) point is straight forward. Any other point and the transformation gets weird. See examples:

Visual reference: positive 90 degrees rotation
positive 90 degrees rotation

Visual reference: negative 90 degrees rotation
negative 90 degrees rotation

Graphics

Create

Create and save a PNG

using System;
using System.Drawing;
using System.Drawing.Imaging;

using (Bitmap bitmap = new Bitmap(50, 50)) 
{
    using (Graphics graphics = Graphics.FromImage(bitmap)) 
    {
        graphics.Clear(Color.Green);
    }
    bitmap.Save("green.png", ImageFormat.Png);
}

Settings

Resolution

bitmap.SetResolution(dpiX, dpiY); 

Anti-Aliasing

using System.Drawing.Drawing2D;

graphics.SmoothingMode = SmoothingMode.AntiAlias; //same as SmoothingMode.HighQuality
graphics.SmoothingMode = SmoothingMode.None; //same as SmoothingMode.HighSpeed

Page Unit

graphics.PageUnit = GraphicsUnit.Display; //usually pixels for video and 1/100 inch for printers
graphics.PageUnit = GraphicsUnit.Document; //1/300 inch
graphics.PageUnit = GraphicsUnit.Inch;
graphics.PageUnit = GraphicsUnit.Millimeter;
graphics.PageUnit = GraphicsUnit.Pixel;
graphics.PageUnit = GraphicsUnit.Point; //for printers, 1/72 inch
graphics.PageUnit = GraphicsUnit.World; //world coordinate system unit

Draw

Draw line

graphics.DrawLine(pen, new Point(x1, y1), new Point(x2, y2));
//or
graphics.DrawLine(pen, x1, y1, x2, y2);

Draw rectangle

graphics.DrawRectangle(pen, new Rectangle(originPoint, new Size(width, height)));
//or
graphics.DrawRectangle(pen, originX, originY, width, height);

Draw image file: cover graphics with repeating image

Image pattern = Image.FromFile(patternFilename);
for(int x = 0; x < bitmap.Width + pattern.Width; x += pattern.Width)
{
    for(int y = 0; y < bitmap.Height + pattern.Height; y += pattern.Height)
    {
        graphics.DrawImage(pattern, x, y);
    }
}

Text

Draw vertical text

Font font = new Font("Times New Roman", 14, FontStyle.Regular);
StringFormat format = new StringFormat();
format.FormatFlags = StringFormatFlags.DirectionVertical;
graphics.DrawString("Text", font, Brushes.Black, new Point(0, 0), format);
Text will be drawn rotated 90* clockwise, with the bottom along the X provided.
Images

Bitmap

Basics:

using(Bitmap bitmap = new Bitmap(width, height))
{
    bitmap.SetResolution(resolutionX, resolutionY);
    using(Graphics graphics = Graphics.FromImage(bitmap))
    {
        graphics.SmoothingMode = SmoothingMode.AntiAlias;
        graphics.PageUnit = GraphicsUnit.Pixel;
        //draw on bitmap
    }
    bitmap.Save("filename.png", ImageFormat.Png);
}

C# bitmap runs on GDI Plus, which imposes size limits. The limit seems to be about here:
(maybe)

//works
Bitmap b = new Bitmap(23170,23170);

//doesn't work
Bitmap b = new Bitmap(23171,23170);
Text Files

Read Text


using System;
using System.IO;

using(StreamReader reader = new StreamReader(filename))
{
    string line = null;
    while((line = reader.ReadLine()) != null)
    {
    }
}

Write Text


using System;
using System.IO;

using(StreamWriter writer = new StreamWriter(filename))
{
    writer.Write("X");
    writer.WriteLine("Y");
}

Operating System Separator

Windows vs Linux use different path or directory separators.

using System.IO;

var path = "folder" + Path.DirectorySeparatorChar + "filename.txt";

Parts Of File Name

get file extension

using System.IO;

string extension = Path.GetExtension("folder/file.txt");


XmlDocument

To convert XmlDocument or XmlNode contents to a string:

using System.IO;
using System.Xml;

StringWriter stringWriter = new StringWriter();
XmlTextWriter xmlTextWriter = new XmlTextWriter(stringWriter);
xmlDocument.WriteTo(xmlTextWriter);
string text = stringWriter.ToString();

//Or
StringWriter stringWriter = new StringWriter();
XmlTextWriter xmlTextWriter = new XmlTextWriter(stringWriter);
xmlNode.WriteTo(xmlTextWriter);
string text = stringWriter.ToString();

Namespace

All elements with be given a default attribute "xmlns=''" unless your specify the namespace. To get an element with no explicit "xmlns" attribute, specify the same namespace as the root element.

using System.Xml;

XmlDocument xmlDocument = new XmlDocument();
XmlNode root = xmlDocument.CreateElement(rootNodeName, namespaceUri);
XmlNode node = xmlDocument.CreateElement(nodeName, root.NamespaceURI);
root.AppendChild(node);
Result:

<rootNodeName xmlns="namespaceUri">
  <nodeName>
  </nodeName>
</rootNodeName>

To add namespace aliases to the root node:

using System.Xml;

XmlDocument xmlDocument = new XmlDocument();
XmlNode root = xmlDocument.CreateElement(rootNodeName, namespaceUri);
XmlAttribute attribute = xmlDocument.CreateAttribute("xmlns:"+alias);
attribute.Value = otherNamespaceUri;
workbookNode.Attributes.Append(attribute);
Result:

<rootNodeName xmlns="namespaceUri" xlmns:alias="otherNamespaceUri">
</rootNodeName>

To specify an aliased namespace for a child node:

using System.Xml;

XmlDocument xmlDocument = new XmlDocument();
XmlNode root = xmlDocument.CreateElement(rootNodeName, namespaceUri);
XmlAttribute attribute = xmlDocument.CreateAttribute("xmlns", alias);
attribute.Value = otherNamespaceUri;
workbookNode.Attributes.Append(attribute);

XmlNode childNode = xmlDocument.CreateElement(alias, childNodeName, otherNamespaceUri);
root.AppendChild(childNode);
Result:

<rootNodeName xmlns="namespaceUri" xlmns:alias="otherNamespaceUri">
  <alias:childNodeName>
  </alias:childNodeName>
</rootNodeName>

To add an attribute under an aliased namespace:

using System.Xml;

XmlDocument xmlDocument = new XmlDocument();
XmlNode root = xmlDocument.CreateElement(rootNodeName, namespaceUri);
XmlAttribute attribute = xmlDocument.CreateAttribute("xmlns", alias);
attribute.Value = otherNamespaceUri;
workbookNode.Attributes.Append(attribute);

XmlNode childNode = xmlDocument.CreateElement(childNodeName, root.NamespaceURI);
XmlAttribute childAttribute = xmlDocument.CreateAttribute(alias, childAttributeName, otherNamespaceUri);
childAttribute.Value = attributeValue;
childNode.Attributes.Append(childAttribute);
root.AppendChild(childNode);
Result:

<rootNodeName xmlns="namespaceUri" xlmns:alias="otherNamespaceUri">
  <childNodeName alias:childAttributeName="attributeValue">
  </childNodeName>
</rootNodeName>


XDocument


using System.Xml.Linq;

XDocument documentA = XDocument.Load(filename);
XDocument documentB = XDocument.Parse("<a><b>text</b></a>");

Loop through direct children:

foreach(XElement element in document.Root.Elements()) //all
{
}
foreach(XElement element in document.Root.Elements(XName)) //all with matching XName
{
}
foreach(XElement element in document.Root.Elements(string)) //all with matching XName.LocalName
{
}
//Elements() method is inherited from XContainer class

Loop through all descendants:

foreach(XElement element in document.Root.Descendants())
{
}

Properties:
XElement.Name = xml tag name
XElement.Value = xml tag inner text

To process XElements that contain plain text mixed with tags:

foreach(XNode node in xElement.Nodes())
{
    //node.NodeType is a System.Xml.XmlNodeType enum value

    //node.NodeType == XmlNodeType.Text for plain text
    //node.ToString() == string value of plain text

    //node.NodeType == XmlNodeType.Element for XElement
    //(node as XElement) casting will work when NodeType is Element
}

To get an attribute value:

string x = xElement.Attribute("attributeName").Value;
//or to protect against null when attribute is not found
string x = xElement.Attribute("attributeName")?.Value;
Excel

EPPlus

Install with NuGet:

install-package EPPlus

Generate excel file:

using OfficeOpenXml;

ExcelPackage newPackage = new ExcelPackage();

ExcelWorksheet worksheet = package.Workbook.Worksheets.Add(name);

worksheet.InsertRow(rowFrom:0, rows:2);
worksheet.Cells["A0"].Value = "Name";
worksheet.Cells["B0"].Value = "Address";
worksheet.Cells["C0"].Value = "Phone";
worksheet.Cells["A1"].Value = "Bob";
worksheet.Cells["B1"].Value = "123 Street";
worksheet.Cells["C1"].Value = "(123) 456-7890";

newPackage.SaveAs(new FileInfo(fullPath));
Database

SQL Server


using System.Data.SqlClient; //in System.Data.dll

Open a connection:

string connectionString = @"Data Source=(LocalDb)\MSSQLLocalDB;Initial Catalog=NameDatabase;Integrated Security=True";
SqlConnection connection = new SqlConnection(connectionString);
connection.Open();

Read-only query:

string commandString = "select count(*) from dbo.Name";
using(SqlCommand command = new SqlCommand(commandString, connection))
{
    SqlDataReader reader = command.ExecuteReader();
    while(reader.Read()) //get next record
    {
        Console.WriteLine("Found {0} names.", reader.GetValue(0)); //get 0th column from record
    }
}

Update query:

string commandString = "update dbo.NameDetail set IsBoy = @IsBoy, IsGirl = @IsGirl where Id = @Id";
using(SqlCommand command = new SqlCommand(commandString, connection))
{
    SqlParameter isBoyParameter = new SqlParameter("IsBoy", isBoy);
    SqlParameter isGirlParameter = new SqlParameter("IsGirl", isGirl);
    SqlParameter idParameter = new SqlParameter("Id", id);

    command.Parameters.Add(isBoyParameter);
    command.Parameters.Add(isGirlParameter);
    command.Parameters.Add(idParameter);

    command.ExecuteNonQuery();
}

Always close your connection when you're finished with it:

connection.Close();

JSON.Newtonsoft

About

You can install JSON.Newtonsoft from NuGet.
It's a widely used library for working with JSON data.


using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

Dynamic Deserialize

Unknown object structure:

using Newtonsoft.Json;

dynamic obj = JsonConvert.DeserializeObject(jsonString);
foreach(dynamic item in obj)
{
    string name = item.Name;
    string value = item.Value;
}

Default Deserialize

Deserialize to known object structure:

using Newtonsoft.Json;

MyType obj = JsonConvert.DeserializeObject<MyType>(jsonString);

Object structure:

public class MyType
{
    public string propertyNameA { get; set; }
    public string propertyNameB { get; set; }
    public string[] arrayNameC { get; set; }
    public MySubType objectNameD { get; set; }
}

public class MySubType
{
    public string propertyNameA { get; set; }
    public string propertyNameB { get; set; }
}

JSON string:

{
    "propertyNameA": "valueA",
    "propertyNameB": "valueB",
    "arrayNameC": [
        "valueC1",
        "valueC2",
        "valueC3"
    ],
    "objectNameD": {
        "propertyNameA": "valueA",
        "propertyNameB": "valueB"
    }
}

Custom Deserialize

You'll need a custom converter if you are handling collections of objects in different formats.

Deserialize with custom converter:

using Newtonsoft.Json;

MyType myType = JsonConvert.DeserializeObject<MyType>(jsonString, new MyTypeConverter());

Custom converter:

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

public class MyTypeConverter : JsonConverter
{
    //convert object to string
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    //return true if this Converter can convert to/from this Type
    public override bool CanConvert(Type objectType)
    {
        return (objectType.Name == "MyType");
    }

    //convert string to object
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if(reader.TokenType == JsonToken.StartObject)
        {
            MyType myType = new MyType();
            JObject root = JObject.Load(reader);

            //conversion code here

            return myType;
        }
        return null;
    }
}

"reader.TokenType" can be any of these JsonToken values:
[JsonToken enum]

The JToken class hierarchy is:
JToken (abstract)
    JContainer (abstract)
        JArray = a JSON array
        JObject = a JSON object
        JProperty = a JSON property key/value pair
        JConstructor (is not part of the JSON specification)
    JValue = a data value (any type: string, long, date, etc)
        JRaw = a raw JSON string
        
Loop through the children of a JContainer:

forach(JToken child in container)
{
}
//or
foreach(JToken child in container.Children())
{
}
//or: to see just children of a particular type
foreach(JProperty property in container.Children<JProperty>())
{
}

Check the name of a JProperty and get the Value:

JProperty property = new JProperty("A", new JValue(1)); 
if(property.Name == "A")
{
    if(property.Value is JValue)
    {
        long value = (long)(property.Value as JValue).Value;
    }
}
Note that all int will be stored as long.
        
Serialize

Example:

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

string result = new JObject( 
    new JProperty("array1", 
        new JArray("a", "b", "c")
    ), 
    new JProperty("obj1", 
        new JObject(
            new JProperty("prop1", new JValue(1)), 
            new JProperty("prop2", new JValue(2))
        )
    )
).ToString();
Results in:

{
    "array1": ["a", "b", "c"],
    "obj1": { 
        "prop1": 1, 
        "prop2": 2 
    } 
) 
Network

Download


using System.Net;

using(WebClient webClient = new WebClient())
{
    webClient.DownloadFile("http://mysite.com/myfile.txt", @"c:\myfile.txt");
}

AutoMapper

AutoMapper is an object-to-object mapper.

[AutoMapper getting started guide]

Cons

I don't recommend AutoMapper because it does not give compile-time errors for using a mapping that is not configured. Instead, it throws a runtime exception: "AutoMapper.AutoMapperMappingException: 'Missing type map configuration or unsupported mapping.'"


MyMapperProfile myMapper = new MyMapperProfile();
ToClass toObject = myMapper.Map<ToClass>(fromObject); //this throws an exception
FluentValidation

FluentValidation is a .Net library for strongly-typed validation rules.

Ex:

using FluentValidation;

public class CustomerValidator : AbstractValidator<Customer> {
    public CustomerValidator() {
        RuleFor(x => x.Surname).NotEmpty();
        RuleFor(x => x.Forename).NotEmpty().WithMessage("First name is required");
        RuleFor(x => x.Discount).NotEqual(0).When(x => x.HasDiscount);
        RuleFor(x => x.Address).Length(20, 250);
        RuleFor(x => x.Postcode).Must(BeAValidPostcode).WithMessage("Postcode is required");
    }

    private bool BeAValidPostcode(string postcode) {
        // custom postcode validating logic goes here
    }
}

Integration

FluentValidation 9.0 requires a netcoreapp application. It does not work with netstandard libraries.
FluentValidation up to 8.6.2 does work with netstandard libraries.

FluentValidation supports integration with ASP.NET Core 2.1 or 3.1 (3.1 recommended). Once enabled, MVC will use FluentValidation to validate objects that are passed in to controller actions by the model binding infrastructure.
[Documentation]

1. Include package FluentValidation.AspNetCore
2. Configure your service to integrate with FluentValidation

public void ConfigureServices(IServiceCollection services) {
    services.AddMvc(setup => {
        // mvc setup
    }).AddFluentValidation();
}
3. Configure your service to use specific Validators you've written

public void ConfigureServices(IServiceCollection services) {
    services.AddMvc(setup => {
        //mvc setup
    }).AddFluentValidation();

    services.AddTransient<IValidator<Person>, PersonValidator>();
}
3b. Or tell the service to use all AbstractValidator subtypes it finds in your assembly

public void ConfigureServices(IServiceCollection services) {
    services.AddMvc(setup => {
        // mvc setup
    }).AddFluentValidation(fv => {
        fv.RegisterValidatorsFromAssemblyContaining(GetType());
    });
}

When validators are executed using this automatic integration, the RootContextData contains an entry called InvokedByMvc with a value set to true, which can be used within custom validators to tell whether a validator was invoked automatically (by MVC), or manually.

Reusable Validators

Write extensions to make reusable rules:

using FluentValidation;

public static class ValidationExtensions
{
    public static IRuleBuilderOptions<T, decimal> ValidDecimal<T>(this IRuleBuilder<T, decimal> ruleBuilder)
        => ruleBuilder.Must(x => x > 0);
}

Testing

[Documentation]

Library includes test extensions:

using FluentValidation;
using FluentValidation.TestHelper;
using Xunit;

public class PersonValidatorTests
{
    [Fact]
    public void NullName_ReturnsError()
    {
        //Arrange
        var validator = new PersonValidator();
        var name = default(string);
        
        //Act Assert
        validator.ShouldHaveValidationErrorFor(person => person.Name, name);

        //checking for errors
        validator
            .ShouldHaveValidationErrorFor(person => person.Name, name)
            .WithErrorMessage("Name is required")
            .WithSeverity(Severity.Error)
            .WithErrorCode("NotNullValidator");

        //checking for a lack of errors
        validator
            .ShouldNotHaveValidationErrorFor(person => person.Name, name)
            .WithoutErrorMessage("Name is required")
            .WithoutSeverity(Severity.Error)
            .WithoutErrorCode("NotNullValidator");
    }

    [Fact]
    public void MinimumName_Success()
    {
        //Arrange
        var validator = new PersonValidator();
        var name = "a";
        
        //Act Assert
        validator.ShouldNotHaveValidationErrorFor(person => person.Name, name);
    }

    [Fact]
    public void Example_of_setting_multiple_fields_at_once()
    {
        //Arrange
        var validator = new PersonValidator();
        var person = new Person() {
            Name = "Francis",
            age = 14
        };
        
        //Act Assert
        validator.ShouldNotHaveValidationErrorFor(p => p.Name, person);
    }

    [Fact]
    public void Example_of_asserting_on_multiple_fields_at_once()
    {
        //Arrange
        var validator = new PersonValidator();
        var person = new Person() {
            Name = "Francis",
            age = 14
        };
        
        //Act
        var result = validator.TestValidate(person);
        
        //Assert
        result.ShouldNotHaveValidationErrorFor(p => p.Name);
        result.ShouldNotHaveValidationErrorFor(p => p.Age);
    }
}
Logging

How to use Microsoft.Extensions.Logging.Abstractions.dll


using Microsoft.Extensions.Logging;

public class MyClass
{
    public MyClass(ILogger<MyClass> logger) //dependency injection setup not included
    {
        logger.LogError("string", object, object);
    }
}

[rsuster blog]
[nblumhardt blog]

Structured Logs

aka Semantic Logs
The fields and their placeholders are sent in separately. This allows the logging tool to display each field separately.

(Testing is based on Seq logs application)


logger.LogWarning("The person {PersonId} could not be found.", personId);
// Message: The person 5 could not be found.
// MessageTemplate: The person {PersonId} could not be found.
// PersonId: 5
The name of the placeholder has no relationship to the name of the object or property value. The values are applied to the placeholders in left-to-right order.
The name of the placeholder is what Seq shows as the field name.

Always use placeholders instead of string interpolation.

Avoid using dots in placeholder names (ex: Device.Id) because some ILogger implementations (such as Serilog) do not support this.

You should build up a list of constant log entry property (placeholder) names for your domain for consistent logging. This is usually set with scopes.

Some object types will show up in Seq with their complete structure and data intact. (ex: Dictionary) This is due to custom handling in Serilog. Most object types will default to their ToString() value (which defaults to the fully qualified name of the class).

Structure-Capturing Operator

"@" is the structure-capturing operator.
Place @ before the placeholder name.

logger.LogError("Class.Method {@Request} {@Errors}", request, result.Errors);

Now all objects will be sent with their full structure and data, instead of some of them being sent as just their ToString() value.
- watch out for circular references and deep structures

Note that this does not work on anonymous objects.

Destructuring

"Destructuring" is the step where an object is converted into a logged property.

You can configure logger with custom destructuring logic.

At the global level:

Log.Logger = new LoggerConfiguration()
    .WriteTo.Console()
    .Destructure.ByTransforming<UserData>(u => new { u.Id, u.Username })
    .CreateLogger();
In this example, all property except Id and Username are ignored when a UserData object is logged. This can protect private data, and stop circular references.

At the class level:

using Destructurama.Attributed;

public class UserData
{
    public string Id { get; set; }
    public string Username { get; set; }
    [NotLogged]
    public Address Address { get; set; }
}

...

Log.Logger = new LoggerConfiguration()
    .WriteTo.Console()
    .Destructure.UsingAttributes()
    .CreateLogger();

See Destructurama for more interesting plug-ins for Serilog.

Exceptions

To log an exception, always pass the exception object as first argument:

logger.LogWarning(exception, "An exception occurred")

The formatting and storage is exception specific if you use the correct method overload.

Scopes

Use scopes to transparently add custom properties to all logs in a given execution context.


using (logger.BeginScope(new Dictionary<string, object> { {"PersonId", 5 } }))
{
    logger.LogInformation("Hello");
    logger.LogInformation("World");
}
Now both log statements will be logged with an additional PersonId property.

MediatR Library

MediatR is a .Net library for the Mediator Pattern.
- "MediatR is essentially a library that allows in process messaging – which in turn allows you to follow the Mediator Pattern."

MediatR can be set to
- request/response sends each message to just one handler
- notification broadcasts messages to multiple handlers

Dot Net Core Tutorials

Getting Started

Install Nuget packages
- MediatR
- MediatR.Extensions.Microsoft.DependencyInjection //to use .Net IOC

In Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddMediatR(Assembly.GetExecutingAssembly());
}
This configuration causes MediatR to find all INotificationHandlers in the project and automatically registers them.

Write some internal messages

//INotification is a MediatR class
public class NotificationMessage : INotification
{
    public string NotifyText { get; set; }
}

Write message handlers

public class Notifier1 : INotificationHandler<NotificationMessage>
{
    public Task Handle(NotificationMessage notification, CancellationToken cancellationToken)
    {
        Debug.WriteLine($"Debugging from Notifier 1. Message  : {notification.NotifyText} ");
        return Task.CompletedTask;
    }
}

public class Notifier2 : INotificationHandler<NotificationMessage>
{
    public Task Handle(NotificationMessage notification, CancellationToken cancellationToken)
    {
        Debug.WriteLine($"Debugging from Notifier 2. Message  : {notification.NotifyText} ");
        return Task.CompletedTask;
    }
}

Abstract out that you are using MediatR so your whole code base is not dependent on it

public interface INotifierMediatorService
{
    void Notify(string notifyText);
}

public class NotifierMediatorService : INotifierMediatorService
{
    private readonly IMediator _mediator;

    public NotifierMediatorService(IMediator mediator)
    {
        _mediator = mediator;
    }

    public void Notify(string notifyText)
    {
        _mediator.Publish(new NotificationMessage { NotifyText = notifyText });
    }
}

//and register this service in ConfigureServices
services.AddTransient<INotifierMediatorService, NotifierMediatorService>();

Use MediatR to send internal messages

public class HomeController : ControllerBase
{
    private readonly INotifierMediatorService _notifierMediatorService;

    public HomeController(INotifierMediatorService notifierMediatorService)
    {
        _notifierMediatorService = notifierMediatorService;
    }

    [HttpGet("")]
    public ActionResult<string> NotifyAll()
    {
        _notifierMediatorService.Notify("This is a test notification");
        return "Completed";
    }
}

Behaviors

MediatR Behaviors

MediatR 3.0 Behaviors allow you to build your own pipeline directly inside of MediatR without resolving to using decorators around your handlers.

A pipeline behavior is an implementation of IPipelineBehavior<TRequest, TResponse>. It represents a similar pattern to filters in ASP.NET MVC/Web API or pipeline behaviors in NServiceBus.

The pipeline behaviors are only compatible with IRequestHandler<TRequest,TResponse> and can't be used with INotificationHandler<TRequest>.

Pipeline behaviors must implement

Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next);
"request" is the request object passed through IMediator.Send.
"next" is an async continuation for the next action in the behavior chain. Either await or return the "next" invocation in your Handle method.

Ex

public class LoggingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
    private readonly ILogger<LoggingBehavior<TRequest, TResponse>> _logger;

    public LoggingBehavior(ILogger<LoggingBehavior<TRequest, TResponse>> logger)
    {
        _logger = logger;
    }

    public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
    {
        _logger.LogInformation($"Handling {typeof(TRequest).Name}");
        var response = await next();
        _logger.LogInformation($"Handled {typeof(TResponse).Name}");

        return response;
    }
}
"request" will be automatically passed into "next".

Register the behaviors in the order you would like them to be called.

cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(OuterBehavior<,>));
cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(InnerBehavior<,>));
cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(ConstrainedBehavior<,>));
MediatR uses the MultiInstanceFactory delegate to resolve the IEnumerable<IPipelineBehavior<TRequest, TResponse>> for whatever closed types your request and responses are. For void/Task requests, the TResponse type will be Unit.

All the behaviors together form one pipeline, in the order they were registered in. But that does not mean every request goes through every behavior. Each behavior defines the Request Data Type it expects. Each request will only be sent to the behaviors that accept that data type.

Requests travel through the full behavior pipeline before they are handed to a single Handler. No matter what order things are registered in, the Handler is always last.

Exceptions

Throw


throw new Exception("Error");

As of C# 7, throw is an expression instead of a statement.
This means you can use throw in more locations in your code, including all the expression bodies that C# 7 also made possible.
Example of now valid usage:

string argument = args.Length > 2 ? args[2] : throw new ArgumentException("Name not found");

Recommended Usage

You can throw:
    NotImplementedException
    InvalidOperationException
    NotSupportedException
    PlatformNotSupportedException
    ArgumentException
    ArgumentNullException
    ArgumentOutOfRangeException
    FileNotFoundException
    DirectoryNotFoundException
    DriveNotFoundException
    PathTooLongException
    UriFormatException
    DivideByZeroException
    FormatException
    IndexOutOfRangeException
    KeyNotFoundException
    ObjectDisposedException
    OverflowException
    RankException (array with wrong number of dimensions)
    TimeoutException
    + any of your custom exceptions

Only the system should throw:
    Exception
    SystemException
    ApplicationException
    NullReferenceException
    AccessViolationException
    IndexOutOfRangeException (?? conflicting suggestions from microsoft ??)
    StackOverflowException
    OutOfMemoryException
    ComException
    SEHException
    ExecutionEngineException
    
Exception Filters

(.Net 4.6)

Only catch an exception type if a condition is true. The condition is evaluated in the current scope.


try
{
}
catch(XException ex) if (condition)
{
}
catch(YException ey) if (condition)
{
}
catch(Exception e)
{
}

Things I've Run Into

MissingMethodException:
    Typically caused by an up-to-date package calling an out-of-date package. Check that you're using the same versions of NuGet packages in projects that call each other.
    
Global Exception Handling

Event System.AppDomain.CurrentDomain.UnhandledException will be triggered when an exception goes unhandled in your application.


using System;

class Program
{
    static void Main(string[] args)
    {
        System.AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionTrapper;
        throw new Exception("Kaboom");
    }

    static void UnhandledExceptionTrapper(object sender, UnhandledExceptionEventArgs e) 
    {
        Console.WriteLine(e.ExceptionObject.ToString());
        Console.WriteLine("Press Enter to continue");
        Console.ReadLine();
        Environment.Exit(1);
    }
}
Debugging

Debugger Display

The DebuggerDisplay specifies what string to display when the object is inspected in the Debugger Display. The default is object.ToString().

You can place this attribute on a class, property, field, or assembly.


[DebuggerDisplay("Order {Id}")]
class Order
{
    private int Id;
}

The text is shown verbatim, and anything in {} is compiled and run in the scope of the object.

These are valid, but not recommended:

[DebuggerDisplay("{count - 2}")]
[DebuggerDisplay("{count++}")] //this actually edits the object

It is recommended that you call one method that handles the string building:

[DebuggerDisplay("GetHtmlString()")]

Two reasons for this:
1. Complex operations can really slow down your debugger.
2. Contents of the DebuggerDisplay attribute are not compiled until debugger time, and then they are compiled in the current language of the program, which may not be the language your class was written in. This can cause strange errors and behavior.
    
"nq" means no quote, wrapping quotes are removed from the string:

[DebuggerDisplay("Order {Id,nq}")]

Compiler Services

What if you are debugging, and you want to know which method called the method you are looking at. Maybe you are only interested in one caller.

You can add an optional parameter to the method you are looking at:

void MyMethod(int x, int y, ..., string caller = null)
And just update the one caller you are interested in to provide that parameter.

Or, even better, you can use compiler services to automatically provide this data (as of .Net 4.5):

using System.Runtime.CompilerServices;
...
void MyMethod(int x, int y, ..., [CallerMemberName] string caller = null)

CompilerServices includes CallerMemberName, CallerLineNumber, and CallerFilePath.

Event Viewer

Windows:
When an installed program is having problems, open Event Viewer > Windows Logs > Applications and see what errors have been logged.

TDD

Test Driven Development says:
When you are assigned a bug to fix, instead of setting breakpoints and stepping through the code, you can write unit tests to isolate the error. Then you fix it. Then you already have the unit tests.
Unit Testing

Test Privates

You can unit test private methods using the PrivateObject.

using Microsoft.VisualStudio.TestTools.UnitTesting;

public class Apple
{
    public Apple()
    {
    }
    
    private int Count()
    {
        return 5;
    }
}

[TestClass]
public class TestClass
{
    [TestMethod]
    public void TestMethod()
    {
        Apple apple = new Apple();
        PrivateObject privateApple = new PrivateObject(apple);
        
        int result = privateApple.Invoke("Count");
        
        Assert.AreEqual(5, result);
    }
}

Test Internals

You can unit test internal classes by marking the original assembly as friendly to the test assembly.

Add this to either the Properties/AssemblyInfo.cs file or to a code file in the assembly.

using System.Runtime.CompilerServices;
[assembly:InternalsVisibleTo("NameOfFriendAssembly")]

Marking multiple assemblies as friendly:

using System.Runtime.CompilerServices;
[assembly:InternalsVisibleTo("NameOfFriendAssemblyA")]
[assembly:InternalsVisibleTo("NameOfFriendAssemblyB")]
//or
[assembly:InternalsVisibleTo("NameOfFriendAssemblyA"), InternalsVisibleTo("NameOfFriendAssemblyB")]

Scripts

These are tricky unit test setups I've had to figure out.

Testing an Asp.Net Core Model Binder

using System.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Primitives;
using Xunit;

//Arrange
var context = new DefaultModelBindingContext()
{
    ModelName = "MyModel",
    ValueProvider = new CompositeValueProvider(new List<IValueProvider>()
    {
        new QueryStringValueProvider(
            new BindingSource("id", "displayName", isGreedy: false, isFromRequest: true),
            new QueryCollection(new Dictionary<string, StringValues>() {
                { "Test", new StringValues("12") }
            }),
            CultureInfo.DefaultThreadCurrentCulture)
    })
};

//Act
await _modelBinder.BindModelAsync(context);

//Assert
Assert.True(context.Result.IsModelSet);
var filterCriteria = Assert.IsType<MyModel>(context.Result.Model);
Assert.NotNull(filterCriteria);

If you need an HttpResponseHeaders object for testing, you can get it by instantiating an HttpResponseMessage.
If you need an HttpContent from an object, see these children of HttpContent:
- ByteArrayContent, FormUrlEncodedContent, StringContent, MultipartContent, ReadOnlyMemoryContent, StreamContent, JsonContent

HttpContent example = JsonContent.Create(new MyObject());

Given a real Controller, how to mock its HttpContext?

public void SetupControllerContext(Controller controller, string bearerToken)
{
    var headerCollection = new Mock<IHeaderDictionary>();
    headerCollection.Setup(m => m[ProblemDetailsController.HEADER_AUTHORIZATION]).Returns(bearerToken);
    headerCollection.Setup(m => m.ContainsKey(It.IsAny<string>())).Returns(true);
    var request = new Mock<HttpRequest>();
    request.SetupGet(x => x.Headers).Returns(headerCollection.Object);
    var httpContext = new Mock<HttpContext>();
    httpContext.SetupGet(x => x.Request).Returns(request.Object);
    var context = new ActionContext(httpContext.Object, new RouteData(), new ControllerActionDescriptor());
    controller.ControllerContext = new ControllerContext(context);
} 
MSTest Testing

General setup

using Microsoft.VisualStudio.TestTools.UnitTesting; //from Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll

[TestClass]
public class TestClass
{
    [TestMethod]
    public void TestMethod()
    {
        //assign
        //act
        //assert
    }
}

Assert

There are many built-in Assert statements.

Any Assert can be given a last string parameter. This string message will be appended to the default error message if the Assert fails.

Setup

It appears that test cases can be run in any order, regardless of how they are grouped in classes,
so don't rely on all methods in one class being run before the next class.

This initialize method is called once before all the selected test cases in this class are run.
Note: It does not seem to be guaranteed that all tests in one class will be run before tests from other classes - I've gotten other tests running before the cleanup of the previous class ran.

[ClassInitialize]
public static void Initialize(TestContext context)
{
}

This initialize method is called once before each test case in this class is run.

[TestInitialize]
public void Initialize()
{
}

Cleanup

Cleanup does not run if Initialize threw an exception.

This cleanup method is called once after all the selected test cases in this class are run.

[ClassCleanup]
public static void Cleanup()
{
}

This cleanup method is called once after each test case in this class is run.

[TestCleanup]
public void Cleanup()
{
}

Exceptions


[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void TestMethod()
{
    //assign
    //act
    //exception expected instead of assert
}

[TestMethod]
[ExpectedException(typeof(ArgumentException), "Custom description of error.")]
public void TestMethod()
{
    //assign
    //act
    //exception expected instead of assert
}

DataTestMethod

Run the same test with different inputs:

using Microsoft.VisualStudio.TestTools.UnitTesting; //from Microsoft.VisualStudio.TestPlatform.TestFramework.dll

[DataTestMethod]
[DataRow(-1)]
[DataRow(0)]
[DataRow(1)]
public void MyTest(int value)
{
    var result = Operation(value);

    Assert.IsFalse(result);
}

Xunit Testing

Install NuGet package: xunit
Install NuGet package: xunit.runner.visualstudio (to run tests in VS Test Window)

Testing with the Xunit library, available through NuGet.

using Xunit;

[Xunit on GitHub]

Fact

The basic way of writing tests.

When your tests are run, this shows up as a single test.


using Xunit;

public class CalculatorTests
{
    [Fact]
    public void TestAdd()
    {
        //arrange
        Calculator calculator = new Calculator();
        int a = 1;
        int b = 2;
        
        //act
        int result = calculator.Add(a, b);
        
        //assert
        Assert.Equal(3, result);
    }
}

Theory

Theories are used to run multiple values through a single test.

When your tests run, this shows up as one test per InlineData.
Remember that tests can be run in any order.

InlineData can only refer to const values and literals. "static readonly" values cannot be used.


using Xunit;

public class CalculatorTests
{
    [Theory]
    [InlineData(1, 2, 3)]
    [InlineData(-4, -6, -10)]
    [InlineData(-2, 2, 0)]
    [InlineData(int.MinValue, -1, int.MaxValue)]
    public void TestAdd(int a, int b, int expectedResult)
    {
        //arrange
        Calculator calculator = new Calculator();
        
        //act
        int result = calculator.Add(a, b);
        
        //assert
        Assert.Equal(expectedResult, result);
    }
}

There is no option for adding a label to an InlineData.

Dedicated Class

If the parameters you need to test are not constants, you can create a dedicated class to provide data to one test method.

When your tests run, this shows up as one test per enumerated value set.
Remember that tests can be run in any order.


using Xunit;

public class CalculatorTests
{
    [Theory]
    [ClassData(typeof(MyTestData))]
    public void TestAdd(int a, int b, int expectedResult)
    {
        //arrange
        Calculator calculator = new Calculator();
        
        //act
        int result = calculator.Add(a, b);
        
        //assert
        Assert.Equal(expectedResult, result);
    }
}

public class MyTestData : IEnumerable<object[]>
{
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

    public IEnumerator<object[]> GetEnumerator()
    {
        yield return new object[] {1, 2, 3};
        yield return new object[] {-4, -6, -10};
        yield return new object[] {-2, 2, 0};
        yield return new object[] {int.MinValue, -1, int.MaxValue};
    }
}

Make sure each value set is independent of each other; for instance, not sharing references to objects. You can get weird bugs in your test cases.

Member Data

If you don't want to create a whole different class, in a different file, you can use a local method instead.

When your tests run, this shows up as one test with "multiple result outcomes". In Visual Studio, this does not display as multiple tests.
Remember that tests can be run in any order.

MemberData cannot refer to any class instance data. All values must be marked const, marked static, or be literals.


using Xunit;

public class CalculatorTests
{
    [Theory]
    [MemberData(nameof(MyTestData))]
    public void TestAdd(int a, int b, int expectedResult)
    {
        //arrange
        Calculator calculator = new Calculator();
        
        //act
        int result = calculator.Add(a, b);
        
        //assert
        Assert.Equal(expectedResult, result);
    }

    public static List<object[]> MyTestData => new List<object[]> {
        new object[] {1, 2, 3},
        new object[] {-4, -6, -10},
        new object[] {-2, 2, 0},
        new object[] {int.MinValue, -1, int.MaxValue}
    };
}

Assert


Assert.Throws<ArgumentException>(() => profiles.GetSettingsForUserID(""));
var exception = Assert.Throws<ArgumentException>(() => profiles.GetSettingsForUserID(""));

await Assert.ThrowsAsync<ArgumentException>(async () => await MethodThatThrows());
await Assert.ThrowsAsync<ArgumentException>(() => MethodThatThrows());

MyType typedVariable = Assert.IsType<MyType>(generalVariable);

Run Parallel Or In Series

(None of this worked me in .Net Framework 4.5.2)
[xUnit documentation]

By default, unit tests run in parallel.

If you're actually writing integration tests that rely on shared state, you can force the tests to run in series.

Run tests within as assembly in series:

[assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)]

Run all tests in series:

[assembly: CollectionBehavior(DisableTestParallelization = true)]

To run select classes in series, give them the same collection name:

[Collection("CollectionName")]
class TestClassA
{
}

[Collection("CollectionName")]
class TestClassB
{
}
Moq

About

Moq is a popular mocking framework for C#. You can install Moq (and its dependencies) with the NuGet Package Manager.

"Mocking" means creating a derived class of the class you want to test, and overriding its methods to respond the way you want. This is used to setup test scenarios without actually setting up your end-to-end system for each scenario. This allows you to test each part of your system in isolation.

If you find yourself mocking a normal class, you should probably refactor so the class implements an interface and you are mocking the interface. See "Dependency Inversion" for more.

A thought I had was, if I have an Interface already, why use Moq? Why not just implement a test version of the Interface?
Answer: With Moq, all the test implementation logic is contained in the test case, and you can easily use a different implementation for each test.

Basic Example


using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;

[TestClass]
public class MyClass
{
    [TestMethod]
    public void MyTest()
    {
        Mock<IMyInterface> mock = new Mock<IMyInterface>();
        //setup the mock here
        //run test here, using mock.Object
        //use verify methods on mock, and/or use asserts
        Assert.AreEqual(mock.Object.Property1, "value");
    }
}

Options

Strict Moq will raise an exception for anything invoked in the mock that was not setup first:

Mock<IMyInterface> mock = new Mock<IMyInterface>(MockBehavior.Strict);

Invoke base class implementation if no expectation was setup to override the member.
See "Partial Mocks".
This is required for mocking Web/Html controls in System.Web.

Mock<IMyInterface> mock = new Mock<IMyInterface>() { CallBase = true };

If some member is called that was not setup, automatically create and return a new Mock for that member. This is default recursive Mocks.

Mock<IMyInterface> mock = new Mock<IMyInterface>() { DefaultValue = DefaultValue.Mock };

Setup Methods

Setup methods will overwrite each other. The last general setup of method A will overwrite all previous setups of method A.
You are allowed to have multiple setups for method A provided they all have different parameter specifications.
If you have some setups for method A with variable specifications, but they don't cover all possible eventualities, the rest of the possible calls will default to returning null.

Method with parameter and return value:

mock.Setup(i => i.Method("parameter")).Returns("result");

Method that will return each value, in order, as it is called multiple times:

mock.SetupSequence(i => i.Method()).Returns("resultA").Returns("resultB").Returns("resultC").Throws(new InvalidOperationException());

//newer version of mock
mock
    .SetupSequence(i => i.Method(It.IsAny<string>(), It.IsAny<int>()))
    .Returns((string s, int i) => {
        if(s == "abc")
            return 5;
        if(s != "def" && 1 < 10)
            return 15;
        return -1;
    });

Method with an out parameter and a return value:

int outParameter = 0;
mock.Setup(i => i.Method("parameter", out outParameter)).Returns("result");

//async
mock.Setup(i => i.Method("parameter", out outParameter)).Returns(Task.FromResult("result"));

Method with a ref parameter:

int refParameter = 0;
mock.Setup(i => i.Method("parameter", ref refParameter));

Throw an exception:

mock.Setup(i => i.Method("parameter")).Throws<InvalidOperationException>();

mock.Setup(i => i.Method("parameter")).ThrowsAsync(new Exception());

Accept any parameter of the correct data type:

mock.Setup(i => i.Method(It.IsAny<string>()));

Accept any parameter that is passed by ref: (As of Moq 4.8)

mock.Setup(i => i.Method(ref It.Ref<MyType>.IsAny));

With argument constraints:
Accept any parameter by type and condition:

mock.Setup(i => i.Method(It.Is<int>(i => i % 2 == 0)));

Accept any parameter by type and range:

mock.Setup(i => i.Method(It.IsInRange<int>(0, 10, Range.Inclusive)));

Accept any parameter by regular expression:

mock.Setup(i => i.MethodCondition5(It.IsRegex("\s+", RegexOptions.IgnoreCase)));

Set a "ref" or "out" value:
Use Moq's Callback to set the desired value after the normal method is run.

public interface IParser
{
    bool TryParse(string value, ref int output);
}

//define a delegate with the same signature as the method under test
delegate void MockTryParseCallback(string value, ref int output);
 
public void TestCase()
{
    var mockParser = new Mock<IParser>();
    mockParser.Setup(x => x.TryParse("255", ref It.Ref<int>.IsAny))
              .Callback(new MockTryParseCallback((string s, ref int output) => output = 255)) //this sets the ref or out value
              .Returns(true);
    // the rest of the test
}

Setup Methods With Expressions

Mocking a method that accepts a Func<Expression> and returning a particular value based on the parameter values passed into the expression:

//example of real method call
string accountId = "a";
string itemId = "b";
MyModel result = await repository.SingleOrDefaultAsync(x => x.AccountId == accountId && x.ItemId == itemId);

//example of mocking the call
string ValidAccountId = "x";
string ValidItemId = "y";
string ValidUserId = "z";
mockRepository
    .Setup(moq => moq.SingleOrDefaultAsync(It.Is<Expression<Func<MyModel, bool>>>(e =>
            e.Compile().Invoke(new MyModel { AccountId = ValidAccountId, ItemId = ValidItemId }))))
    .Returns(Task.FromResult<MyModel>(new MyModel() { UserId = ValidUserId }));
    
//specifies the successful case, where both fields match valid values
//by default, all other cases will just return null
//behavior verified by stepping through test cases
The default behavior (with no setup) is to return null.
Be aware that doing multiple setups of the same expression method is flaky as hell. Best to do one setup for each test, and as long as you don't get null back, the setup worked.

I've tried this simpler syntax, but it returns null in all cases. Do not use.

//example of real method call
string accountId = "a";
string itemId = "b";
MyModel result = await repository.SingleOrDefaultAsync(x => x.AccountId == accountId && x.ItemId == itemId);

//example of mocking the call
string ValidAccountId = "x";
string ValidItemId = "y";
string ValidUserId = "z";
mockRepository
    .Setup(moq => moq.SingleOrDefaultAsync(x => x.AccountId == ValidAccountId && x.ItemId == ValidItemId))
    .Returns(Task.FromResult<MyModel>(new MyModel() { UserId = ValidUserId }));
//Do not use. Does not return the specified value.

Setup Properties

A property value:

mock.Setup(i => i.Property1).Returns("value");

Mock a hierarchy of objects with one command:

mock.Setup(i => i.Property2.SubProperty3.SubSubProperty4).Returns("value");

Wait for this value to be set during the test:

mock.Setup(i => i.Property3 = "value");

Setup stub: track gets and sets of this property during the test:

mock.SetupProperty(i => i.Property5);
mock.SetupProperty(i => i.Property5, "defaultValue");

Setup all stubs: create stubs for all properties at once:

mock.SetupAllProperties();

Verification

Method was not called:

mock.Verify(i => i.Method("parameter"), Times.Never());

Method was called at least once:

mock.Verify(i => i.Method("parameter"), Times.AtLeastOnce());

Method was called with any value of type:

mock.Verify(i => i.Method(It.IsAny<string>()));

Method was called with particular property in value:

mock.Verify(i => i.Method(It.Is<MyObject>(x => x.MyProperty == "value")));

Property was got:

mock.VerifyGet(i => i.Property1);

Property was set:

mock.VerifySet(i => i.Property1);

Property was set to specific value:

mock.VerifySet(i => i.Property1 = "value");

Property was set to something in this range:

mock.VerifySet(i => i.Property1 = It.IsInRange(0, 10, Range.Inclusive));

No other invocations were made, except those already verified:

mock.VerifyNoOtherCalls();

If you get an error about extension methods not being supported, check that you have the right parameters in the Verify(i => i.Method()). If the method signature is not recognized, Moq assumes it is an extension method (which are not supported).

Errors

Error: "Mock does not contain a definition for Setup".
Solution: use strongly-typed Mock

//use this
Mock<IMyInterface> mock = new Mock<IMyInterface>();
//instead of this
Mock mock = new Mock<IMyInterface>();

Scripts

Tricky mocks I've had to figure out.

To mock returning a Task aka Task<void>:

mock.Setup(m => m.Method()).Returns(Task.FromResult<object>(null));

Asp.Net Core MVC: test a Controller Action's use of Headers

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Routing;
using Moq;

//concrete controller
var controller = new MyController();

//mock 
var bearerToken = "bearerToken";
var request = new Mock<HttpRequest>();
request.SetupGet(x => x.Headers["Authorization"]).Returns(bearerToken);
var httpContext = new Mock<HttpContext>();
httpContext.SetupGet(x => x.Request).Returns(request.Object);
var context = new ActionContext(httpContext.Object, new RouteData(), new ControllerActionDescriptor());

controller.ControllerContext = new ControllerContext(context);
//can now run a Controller Action and see what it does with the header

You cannot mock a System.Net.Http.HttpContent class
But you can create a concrete class using of these subclasses
[HttpContent Concrete Classes]
Ex:

var myObject = new MyObject();
var httpContent = JsonContent.Create(myObject);

Patterns To Avoid

Class implements Interface.
In testing, you'll Mock the Interface.
Method on Class makes changes to a reference-type parameter.
So the code works when it is run for real, but the unit test fails because the Mock does not update the reference-type parameter.

Service-level Tests

(not a recognized level of testing, but all that nomenclature is mixed up anyway)

Here, service tests means unit tests built up to cover end-to-end within a single service. So it uses real implementations for all dependency injection, up to the service boundary.

Not sure how much of this is specific to the architecture I pulled it from, and how much is general to C#.

Framework:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Moq;

public class MockLoggerFactory
{
    private readonly ConcurrentDictionary<Type, Mock<ILogger>> _loggers = new ConcurrentDictionary<Type, Mock<ILogger>>();
    
    public IEnumerable<(Type Type, Mock<ILogger> Logger)> Loggers = _loggers.Select(kv => (kv.Key, kv.Value));
    
    public Mock<ILogger>? GetMockLogger<T>() => GetMockLogger(typeof(T));
    
    public Mock<ILogger>? GetMockLogger(Type type) => _loggers.TryGetValue(type, out var mock) ? mock : null;
    
    public ILogger GetOrCreateLogger<T>() => _loggers.GetOrAdd(typeof(T), t => new Mock<ILogger>()).Object;
    
    public static void Add(IServiceCollection services, ServiceLifetime lifetime = ServiceLifetime.Scoped)
    {
        services.Add(new ServiceDescriptor(
            typeof(MockLoggerFactory),
            typeof(MockLoggerFactory),
            lifetime));
        services.Add(new ServiceDescriptor(
            typeof(ILogger<>),
            typeof(MockLogger()),
            lifetime));
    }
    
    private class MockLogger<T> : ILogger<T>
    {
        private readonly ILogger _logger;
        
        public MockLogger(MockLoggerFactory factory) => _logger = factory.GetOrCreateLogger<T>();
        
        public void Log<TState>(
            LogLevel logLevel,
            EventId eventId,
            TState state,
            Exception? exception,
            Func<TState, Exception?, string> formatter) =>
            _logger.Log(logLevel, eventId, state, exception, formatter);
            
        public bool IsEnabled(LogLevel logLevel) => _logger.IsEnabled(logLevel);
        
        public IDisposable BeingScope<TState>(TState state) => _logger.BeingScope(state);
    }
}

Framework:

using System;
using Autofac.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;

public static class AutofacExtensions
{
    private static readonly AutofacServiceProviderFactory _factory = new AutofacServiceProviderFactory();
    
    public static IServiceProvider BuildAutofacServiceProvider(this IServiceCollection services) =>
        _factory.CreateServiceProvider(_factory.CreateBuilder(services));
}

Framework:

using System;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Moq;

public static class ServiceMockExtensions
{
    public static IServiceCollection MockSingleton<T>(this IServiceCollection services, Mock<T>? mock = null) where T : class
    {
        mock ??= new Mock<T>();
        return services.AddSingleton(mock).AddSingleton(s => mock.Object);
    }
    
    public static IServiceCollection MockScoped<T>(this IServiceCollection services) where T : class
    {
        return services.AddScoped((IServiceProvider s) => new Mock<T>()).AddScoped((IServiceProvider s) => s.GetRequiredService<Mock<T>>().Object);
    }
    
    public static IServiceCollection MockScopedOptions<T>(this IServiceCollection services, Func<T> factory) where T : class, new()
    {
        Mock<IOptions<T>> mock = new Mock<IOptions<T>>();
        mock.Setup((IOptions<T> s) => s.Value).Returns(factory);
        return services.AddSingleton((IServiceProvider s) => mock).AddScoped((IServiceProvider s) => mock.Object).AddScoped((IServiceProvider s) => s.GetRequiredService<IOptions<T>>().Value);
    }
}

Framework:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

public static class HostStartupExtensions
{
    public static IServiceCollection WithSettings<TSettings>(this IServiceCollection serviceCollection, IConfiguration configuration) where TSettings : BaseSettings<TSettings>
    {
        return serviceCollection.Configure(delegate (TSettings instance)
        {
            configuration.GetSection(BaseSettings<TSettings>.Name).Bind(instance);
        });
    }
}

Framework:

using System;
using Microsoft.Extensions.DependencyInjection;
using Moq;

public static class ServiceProviderExtensions
{
    public static Mock<T> GetMock<T>(this IServiceProvider services) where T : class
    {
        return services.GetRequiredService<Mock<T>>();
    }
    
    public static T CreateInstance<T>(this IServiceProvider services)
    {
        return ActivatorUtilities.CreateInstance<T>(services, Array.Empty<object>());
    }
}

Framework:

using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Net.Http.Headers;
using Moq;
using NodaTime;
using NodaTime.Extensions;

public class StartupFixtures<TStartup> where TStartup : IStartup
{
    public IStartup Startup { get; }
    
    public IServiceProvider ApplicationServices { get; }
    
    public StartupFixtures(Action<IServiceCollection> mockServices, IConfiguration? config = null)
    {
        config ??= new ConfigurationBuilder()
            .AddInMemoryCollection(new Dictionary<string, string>())
            .Build();
        var hostingEnv = new Mock<IWebHostEnvironment>();
        hostingEnv
            .Setup(m => m.EnvironmentName)
            .Returns("Development");
        Startup = (TStartup)Activator.CreateInstance(
            typeof(TStartup),
            config,
            hostingEnv.Object,
            new NullLoggerFactory()
            }!;
        if(typeof(TStartup).GetProperty("UnderTest", typeof(bool)) is { CanWrite:true } prop)
        {
            prop.SetValue(Startup, true);
        }
        var services = new ServiceCollection();
        services.MockSingleton(hostingEnv);
        services.AddSingleton(SystemClock.Instance.InUtc());
        services.AddSingleton(config);
        Startup.ConfigureServices(services);
        services.AddScope<HttpContext>(svc =>
        {
            var context = new DefaultHttpContext();
            context.Request.Headers[HeaderNames.Authorization] = "foo";
            return context;
        });
        MockLoggerFactory.Add(services);
        mockServices(services);
        ApplicationServices = services.BuildAutofacServiceProvider();
    }
    
    public IServiceScope CreateScope() => ApplicationServices.CreateScope();
}

Framework:

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using NServiceBus; //for events
using NServiceBus.UniformSession;
//using lots of local libraries
using StatsdClient; //for DataDog

public class StartupFixtures : StartupFixtures<Server.Startup>
{
    public StartupFixtures(IConfiguration config = null) : base(MockServices, config) {}

    //first runs real Startup config setup, then this MockServices, then the test-level mockReplacement
    public StartupFixtures(Action<IServiceCollection> mockReplacement, IConfiguration config = null) : base(s =>
    {
        MockServices(s);
        mockReplacement(s);
    }, config) {}
    
    private static void MockServices(IServiceCollection services)
    {
        //override real Startup with default mocks for everything at the service bourdary
        //sample lines
        services.MockScoped<IZInterface>();
        services.AddSingleton<IMessageSession>();
        services.AddSingleton<IUniformSession>();
    }
}

Locally defined IStartup, just the gist of it

using System.Net;
using System.Net.Sockets;
using System.Reflection;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.CookiePolicy;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
//using lots more local infrastructure
public abstract class BaseStartup : IStartup
{
    protected IConfiguration Configuration { get; }
    protected IWebHostEnvironment HostingEnvironment { get; }
    protected ILoggerFactory LoggerFactory { get; }
    
    //lots of specifics omitted
}

using System.Reflection;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
//using lots more local infrastructure
public class Startup : BaseStartup
{
    public Startup(
        IConfiguration configuration,
        IWebHostEnvironment hostingEnvironment,
        ILoggerFactory loggerFactory)
        : base(configuration, hostingEnvironment, loggerFactory, SERVICE_NAME)
        {
        }
        
    public override void ConfigureServices(IServiceCollection services)
    {
        //setup dependency injection - a sampling of commands
        services.WithSettings<XSettings>(Configuration); //options/settings imported
        services.AddScoped<IYInterface, YServices>(); //setup dependency injection of local services
    }
}

Framework:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;

public static class MockLoggingExtensions
{
    public static void VerifyNoOtherLogs(this IServiceProvider services)
    {
        MockLoggerFactory requiredService = services.GetRequiredService<MockLoggerFactory>();
        foreach(var logger in requiredService.Loggers)
        {
            logger.Logger.VerifyNoOtherCalls();
        }
    }

    //Auto-verify logs of all levels you don't care about
    //Then verify that no log levels you do care about have been missed
    public static void VerifyNoOtherLogs(this IServiceProvider services, params LogLevel[] logLevels)
    {
        LogLevel[] loglLevels2 = logLevels;
        LogLevel[] source = (LogLevel[])Enum.GetValues(typeof(LogLevel));
        IEnumerable<LogLevel> enumerable = source.Where((LogLevel x) => !logLevels2.Contains(x));
        MockLoggerFactory requiredServices = services.GetRequiredService<MockLoggerFactory>();
        foreach(var logger in requiredService.Loggers)
        {
            foreach(LogLevel logLevel in enumerable)
            {
                int count = logger.Logger.Invocations.Where((IInvocation invocation) => invocation.Arguments.First().ToString() == logLevel.ToString()).Count();
                logger.Logger.VerifyLog(logLevel, count);
            }
            logger.Logger.VerifyNoOtherCalls();
        }
    }
}

Service Tests

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Moq;
using NodaTime;
using NServiceBus;
using NServiceBus.UniformSession;
using Xunit;

public class ServiceTests
{
    private readonly StartupFixtures _fixtures;
    private readonly Mock<IMessageSession> _messageBus;
    private readonly Mock<IUniformSession> _uniformMessageBus;
    private readonly Mock<IOptions<XSettings>> _mockXSettings = new();
    
    public ServiceTests()
    {
        _fixtures = new StartupFixture(s =>
        {
            s.MockScopedOptions(() => _mockXSettings.Object.Value);
            s.AddScoped<ConcreteClass>(); //something that was not auto-included in service setup
        });
        _messageBus = _fixtures.ApplicationServices.GetMock<IMessageSession>();
        _uniformMessageBus = _fixtures.ApplicationServices.GetMock<IUniformSession>();
    }
    
    private async Task RunTest(Func<IServiceProvider, XController, Task> controllerMethod)
    {
        using var scope = _fixtures.CreateScope();
        var services = scope.ServiceProvider;
        var controller = services.CreateInstance<XController>();
        await controllerMethod(services, controller);
        
        _messageBus.VerifyNoOtherCalls();
        _uniformMessageBus.VerifyNoOtherCalls();
        _messageBus.Invocations.Clear();
        _uniformMessageBus.Invocations.Clear();
    }

    [Fact]
    public async Task ActualTest()
    {
        await RunTest(async (services, controller) =>
        {
            services.SetupMocksForThisTest...
        
            var response = await controller.XEndpoint(xArguments);
            
            //assert something
        });
    }
}
Reflection

Assembly

Load an assembly:

Assembly assembly = Assembly.LoadFrom(assemblyFilename);
This locks the *.dll file. When used through MSBuild, the lock is not released.

Load an assembly without locking the file:

Assembly assembly = Assembly.Load(File.ReadAllBytes(assemblyFilename));
Because of the way this is loaded, any libraries referenced by this one will not be resolvable.
Even if the secondary library is already loaded, it will not be recognized as the referenced library.

Enabling "shadow copying" on the app domain will solve both problems.
This requires creating a whole new app domain because existing ones cannot be edited.

using System;
//...
AppDomainSetup newDomainSetup = new AppDomainSetup();
newDomainSetup.ShadowCopyFiles = "true";

AppDomain newDomain = AppDomain.CreateDomain("", AppDomain.CurrentDomain.Evidence, newDomainSetup);

// Run an executable
newDomain.ExecuteAssembly("path\\program.exe", AppDomain.CurrentDomain.Evidence, commandLineArguments);
//OR run a library method
newDomain.CreateInstance("assemblyName", "typeNameInLibrary");

//...The library type must extend MarshalByRefObject
using System;
using System.Reflection;
public class MyInstance : MarshalByRefObject
{
    public MyInstance()
    {
        //your code here
    }
}
"Shadow copying" means that referenced *.dll files are copied to a temporary directory before being loaded.
This way they are fully loaded without locking the original *.dll file.

Another alternative is to force the current app domain to close when you are done with it. This will release all locked files.
It will only work if it is the last thing done by the application.

public class MyClass
{
    public MyClass()
    {
        Assembly assembly = Assembly.LoadFrom("assemblyFilename");
    }
    
    ~MyClass()
    {
        AppDomain.Unload(AppDomain.CurrentDomain);
    }
}

NameOf

Succinct reflection. Works on classes, methods, properties, and variables.

As of C# 6.0 (.Net Framework 4.6)


public class MyClass
{
    Public double Cost;
}

private string MyMethod(int x)
{
    Return String.Format("{0} {1} {2} {3}", nameof(MyClass), nameof(MyClass.Cost), nameof(MyMethod), nameof(x));
}
//returns “MyClass Cost MyMethod x”

Threads

Requires library System.Threading.dll

Threads will run a specified method. The method must have no parameters and no return type.

Create a new thread:

using System;
using System.Threading;

Thread thread = new Thread(new ThreadStart(MyMethod));
thread.Start();

Thread.Join() will block the current thread until the joined thread completes.
A thread must be started before it can be joined.

To see the id of the current thread:

Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
This can be used to verify that an operation is/isn't running in a different thread.

BackgroundWorker

BackgroundWorker is suggested as an easy way to use threading with Windows Forms, so the GUI thread does not freeze.

BackgroundWorker will run an operation on a different thread and then call an event when it is complete.

Note: do not access any GUI objects from these other threads. You'll freeze your GUI.


using System;
using System.ComponentModel;
using System.Threading;

BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(MyTask); //set that task to run on this new thread
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(OnComplete); //trigger event when thread completes

worker.ProgressChanged += new ProgressChangedEventHandler(OnProgressChanged); //optional: report progress to GUI thread
worker.WorkerReportsProgress = true;

worker.WorkerSupportsCancellation = true; //can stop thread partway

worker.RunWorkerAsync(); //start the new thread asynchronously from this one

void OnComplete(object sender, RunWorkerCompletedEventArgs e)
{
    if(e.Error != null)
    {
        //handle exception (you cannot access e.Result or worker.ReportProgress if there is an e.Error)
        return;
    }
    if(e.Canceled)
    {
        //handle cancellation (you cannot access e.Result or worker.ReportProgress if worker is canceled)
        return;
    }
    //on thread completed
    //note that the BackgroundWorker object does not exist by this point
    //you can set a Result object on the DoWorkEventArgs that will flow to here in RunWorkerCompletedEventArgs.Result
}

void OnProgressChanged(object sender, ProgressChangedEventArgs e)
{
    myProgressBar.Value = e.ProgressPercentage;
}

void MyTask(object sender, DoWorkEventArgs e)
{
    BackgroundWorker worker = (sender as BackgroundWorker);
    for(int i=0; i<100; i++)
    {
        Thread.Sleep(100); //current thread sleeps for 100 milliseconds
        worker.ReportProgress(i); //report progress from 0% to 100% as integer
        if(worker.CancellationPending)
        {
            e.Cancel = true;
            worker.ReportProgress(0);
            return;
        }
    }
    worker.ReportProgress(100);
}

void OnClickCancellationButton(object sender, EventArgs e)
{
    if(worker.IsBusy)
    {
        worker.CancelAsync();
    }
}

Canceling the background worker is not instantaneous. The worker must periodically check for the pending cancellation, which may take time. Here's the recommended design.

BackgroundWorker worker;

void StartTask()
{
    if(worker != null && worker.IsBusy)
    {
        worker.CancelAsync();
        
        while(worker.IsBusy)
        {
            Application.DoEvents(); //this lets the UI thread continue
        }
    }
    
    worker = new BackgroundWorker();
    worker.DoWork += new DoWorkEventHandler(OnDo);
    worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(OnCompleted);
    worker.WorkerSupportsCancellation = true;
    worker.RunWorkerAsync();    
}

void OnDo(object sender, DoWorkEventArgs e)
{
    while(doAction)
    {
        if(worker.CancellationPending)
        {
            e.Cancel = true;
            return;
        }
        //work
    }        
}

To pass data into the DoWork method:

worker.RunAsync(dataObject);
//...
private void DoWork(object sender, DoWorkEventArgs e)
{
    MyData data = (e.Argument as MyData);
}

When using worker.ReportProgress, do not run it unless you have a new progress value (int 0 to 100). Every time you call it, (if the GUI thread reacts) it blocks the GUI thread.

Application

To catch all uncaught exceptions that occur in an application:

using System.Threading;
static class MyProgram
{
    [STAThread]
    static void Main()
    {
        Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
        MyForm form = new MyForm();
        Application.Run(form);
    }
}
//....
public class MyForm
{
    public MyForm()
    {
        //this must be set before Application.Run(form)
        Application.ThreadException += new ThreadExceptionEventHandler(OnSystemException);
    }
    private void OnSystemException(object sender, ThreadExceptionEventArgs e)
    {
        //handle e.Exception
    }
}
Confirmed this works on System.TargetInvocationException.
Async

C# 7 ValueTask

Asynchronous methods can return a ValueTask<T> object.
This improved performance in some use cases.

C# 7.1 Async Main

You can make a Main method asynchronous now.

Previously:

static int Main()
{
    return DoAsyncWork().GetAwaiter().GetResult();
}

Now:

static async Task<int> Main()
{
    return await DoAsyncWork();
    //or just put the body of DoAsyncWork here
}
PostSharp

PostSharp is a tool that post-processes your compiled code. The final product is still compiled code.
[How it works]

C# does not currently support anything like the "method as decorator" syntax from Python, which enables you to easily mark a method such that it will run another method before/after itself.

PostSharp, which processes your code after it is compiled, supports this.

To add PostSharp to your solution with NuGet:

install-package PostSharp

PostSharp will run automatically as part of building your solution.

OnMethodBoundaryAspect

Create an aspect class with the custom functionality you want:

[PSerializable]
public class LoggingAspect : OnMethodBoundaryAspect
{
    public override void OnEntry(MethodExecutionArgs args)
    {
        Console.WriteLine("The {0} method has been entered.", args.Method.Name);
    }

    public override void OnSuccess(MethodExecutionArgs args)
    {
        Console.WriteLine("The {0} method executed successfully.", args.Method.Name);
    }

    public override void OnExit(MethodExecutionArgs args)
    {
        Console.WriteLine("The {0} method has exited.", args.Method.Name);
    }     

    public override void OnException(MethodExecutionArgs args)
    {
        Console.WriteLine("An exception was thrown in {0}.", args.Method.Name);
    }
}

Add this class as an attribute to any method.

[LoggingAspect]
private void MyMethod()
{
    Console.WriteLine("Hello, world.");
}

Executing this example will print:

The Main method has been entered.
Hello, world.
The Main method executed successfully.
The Main method has exited.

"MethodExecutionArgs.Method" gives you reflection information about the method being decorated.
Pragma Warning

Place at the top of a file so that XML comments are NOT required on public members:

 #pragma warning disable 1591
Style

Thoughts on code style standards. Nothing here is absolute, just my opinions with anecdotal evidence.

(Note that my perferences are always overriden by the code style standards set by my team. Always follow your team's standards.)

Var

I don't use "var" anywhere I can explicitly state the Type.

Using "var" everywhere hides the rare places it is actually needed.

It is harder to read the code to find out what types are being used.
Especially when the variable is storing a return type. Now I have to hover my mouse for Visual Studio to tell me what the type is. Much faster just to read it.

I don't mind typing in the explicit Type. I write it once, I read it many many times. Any style convention that just tries to reduce the number of characters I type is suspicious.

I see it being used inconsistently when devs are not familiar with the "default" keyword. And using "default" this way is clunky and only required to use "var" consistently.

string x = null;
//versus
var x = default(string);

It is impossible to use 3rd party tools to search the code.
- For instance, I've worked on a microservices system where each microservice was one .Net Solution. The Solutions used shared libraries. Visual Studio does not support "Find all references" across Solution boundaries, so I used Windows "findstr" (grep). But since the coding standard was to use "var" everywhere, there were searches I could not perform. The string I was looking for just wasn't in the code.

It is nearly impossible to work outside of an IDE like Visual Studio while using "var" everywhere. This limits choice.

(I was going to say that Visual Studio quick navigation commands are limited with "var", but I just tested that and it works the same.)

C# is a strongly typed language and I dislike seeing that obscured. I like that C# has strict rules because it keeps the code and the design clean.

Expression Bodies


//Expression Body
public int Sum(int a, int b) => a + b;

//Block Body
public int Sum(int a, int b)
{
    return a + b;
}

I'm ok with Expression Bodies for Properties when only Get is implemented and it is simple. I still prefer Block Body in these cases.
I dislike Expression Bodies everywhere else.

I think it's harder to read.

I dislike that I have to restructure the Method/whatever to add a second line of code to it. It obscures what was edited in Git comparisons.

I dislike that some Methods/whatever will be formatted one way and some another, based on how many lines of code they contain.

Tuples

I don't like tuples in C#.
They're great in Python, because Python is a loosely-typed mess.
C# is strongly-typed. Know what your return values are and make a Type for them.

The way they're implemented even feels like a compromise.
You can define field names on tuples, which greatly improves their legibility. But then you're one tiny jump away from defining a Type. Just define the Type.

public (string name, int age) Get()
{
    return ("Bob", 30);
}

Intellisense

Intellisense is a really useful tool that I appreciate having about once a year. The rest of the time I turn it off.

Its popups cover up the section of code I am currently working in. It frequently covers up something I was referencing for the current line of code.

I type fast, and Intellisense flickers its suggestions to keep up. That's annoying.
- It also slows me down to read the suggestions and think "yes, that's the one I want" instead of just typing it myself.

It's quick to fill-in text for me, which means I have to undo its changes. This most often happens when the Type/Namespace/Variable I am referencing is not fully set up. So maybe I need to add a using statement, so what, I can do that after.

I learn less of the code when I have Intellisense turned on.

Auto-completion of braces is the worst.
I've never had trouble closing my own braces. This auto-complete trips up my reflexes everytime.

Wish List

Idle thoughts.

For the common method format of "the entire thing is wrapped in a try/catch", it'd be cool if C# supported this sort of syntax.

public MyType Operation()
{
    DoSomething();
}
catch(ArgumentException ex)
{
    Cleanup();
}
catch(Exception ex)
{
    OtherThing();
}
See, the compiler converts it into a try/catch around the entire body of the method, so the catches have full access to local variables.
The benefit is removing clutter from the method so we can visually focus on the happy-path logic.
"But what about when you have try/catch around just part of the method?" -> decompose that functionality.

For the common method format of "a bunch of guard clauses / data validation before we get to the main operation", we could do something similar.

[Guard(ValidForOperation, a, ArgumentException)]
[Guard(ValidForOperation, b, return)]
public void Operation(TypeA a, TypeB b)
{
    DoSomething();
}

private bool ValidForOperation(TypeA a)
{
    return (a.x != null && a.y > 100 && !String.IsNullOrEmpty(a.z));
}

private bool ValidForOperation(TypeB b)
{
    if(b.x) return ((b.y == EnumY.Y1 || b.y == EnumY.Y2) && b.z != null);
    else return (b.y == Enum.Y3);
}
I'm not convinced by this one, actually. Since the main method body doesn't get nested, it's not such a problem.

What I'd really like is more support for Aspect Oriented Programming.
Why do I need to use a 3rd party service to add logging before/after marked methods? It'd be easier and cleaner for the CLR to handle that.

[Logging]
private TypeA Operation(int b, string c)
{
    return DoSomething();
}
Auto-log the method name and parameter values on entry.
Auto-log the method name, parameter values, and return value on exit.
All it has to do is compile the above into:

private TypeA Operation(int b, string c)
{
    Log("Method Start: {name}, parameters {parameters}, {datetime}", MethodBase.GetCurrentMethod(), b, c, DateTime.Now);
    TypeA result = DoSomething();
    Log("Method End: {name}, parameters {parameters}, returns {result}, {datetime}", MethodBase.GetCurrentMethod(), b, c, result, DateTime.Now);
    return result;
}
Could set the message format when the logging is configured.