C# Language

Keywords

Introduction#

Keywords are predefined, reserved identifiers with special meaning to the compiler. They cannot be used as identifiers in your program without the @ prefix. For example @if is a legal identifier but not the keyword if.

Remarks#

C# has a predefined collection of “keywords” (or reserved words) which each have a special function. These words can not be used as identifiers (names for variables, methods, classes, etc.) unless prefixed with @.

Apart from these, C# also uses some keywords to provide specific meaning in code. They are called contextual keywords. Contextual keywords can be used as identifiers and doesn’t need to be prefixed with @ when used as identifiers.

stackalloc

The stackalloc keyword creates a region of memory on the stack and returns a pointer to the start of that memory. Stack allocated memory is automatically removed when the scope it was created in is exited.

//Allocate 1024 bytes. This returns a pointer to the first byte.
byte* ptr = stackalloc byte[1024];

//Assign some values...
ptr[0] = 109;
ptr[1] = 13;
ptr[2] = 232;
...

Used in an unsafe context.

As with all pointers in C# there is no bounds checking on reads and assignments. Reading beyond the bounds of the allocated memory will have unpredictable results - it may access some arbitrary location within memory or it may cause an access violation exception.

//Allocate 1 byte
byte* ptr = stackalloc byte[1];

//Unpredictable results...
ptr[10] = 1;
ptr[-1] = 2;

Stack allocated memory is automatically removed when the scope it was created in is exited. This means that you should never return the memory created with stackalloc or store it beyond the lifetime of the scope.

unsafe IntPtr Leak() {
    //Allocate some memory on the stack
    var ptr = stackalloc byte[1024];

    //Return a pointer to that memory (this exits the scope of "Leak")
    return new IntPtr(ptr);
}

unsafe void Bad() {
     //ptr is now an invalid pointer, using it in any way will have
     //unpredictable results. This is exactly the same as accessing beyond
     //the bounds of the pointer.
     var ptr = Leak();
}

stackalloc can only be used when declaring and initialising variables. The following is not valid:

byte* ptr;
...
ptr = stackalloc byte[1024];

Remarks:

stackalloc should only be used for performance optimizations (either for computation or interop). This is due to the fact that:

  • The garbage collector is not required as the memory is allocated on the stack rather than the heap - the memory is released as soon as the variable goes out of scope
  • It is faster to allocate memory on the stack rather than the heap
  • Increase the chance of cache hits on the CPU due to the locality of data

volatile

Adding the volatile keyword to a field indicates to the compiler that the field’s value may be changed by multiple separate threads. The primary purpose of the volatile keyword is to prevent compiler optimizations that assume only single-threaded access. Using volatile ensures that the value of the field is the most recent value that is available, and the value is not subject to the caching that non-volatile values are.

It is good practice to mark every variable that may be used by multiple threads as volatile to prevent unexpected behavior due to behind-the-scenes optimizations. Consider the following code block:

public class Example
{
    public int x;

    public void DoStuff()
    {
        x = 5;

        // the compiler will optimize this to y = 15
        var y = x + 10;

        /* the value of x will always be the current value, but y will always be "15" */
        Debug.WriteLine("x = " + x + ", y = " + y);
    }    
}

In the above code-block, the compiler reads the statements x = 5 and y = x + 10 and determines that the value of y will always end up as 15. Thus, it will optimize the last statement as y = 15. However, the variable x is in fact a public field and the value of x may be modified at runtime through a different thread acting on this field separately. Now consider this modified code-block. Do note that the field x is now declared as volatile.

public class Example
{
    public volatile int x;

    public void DoStuff()
    {
        x = 5;

        // the compiler no longer optimizes this statement
        var y = x + 10;

        /* the value of x and y will always be the correct values */
        Debug.WriteLine("x = " + x + ", y = " + y);
    }    
}

Now, the compiler looks for read usages of the field x and ensures that the current value of the field is always retrieved. This ensures that even if multiple threads are reading and writing to this field, the current value of x is always retrieved.

volatile can only be used on fields within classes or structs. The following is not valid:

public void MyMethod()
{
    volatile int x;
}

volatile can only be applied to fields of following types:

  • reference types or generic type parameters known to be reference types
  • primitive types such as sbyte, byte, short, ushort, int, uint, char, float, and bool
  • enums types based on byte, sbyte, short, ushort, int or uint
  • IntPtr and UIntPtr

Remarks:

  • The volatile modifier is usually used for a field that is accessed by multiple threads without using the lock statement to serialize access.
  • The volatile keyword can be applied to fields of reference types
  • The volatile keyword will not make operating on 64-bit primitives on a 32-bit platform atomic. Interlocked operations such as Interlocked.Read and Interlocked.Exchange must still be used for safe multi-threaded access on these platforms.

fixed

The fixed statement fixes memory in one location. Objects in memory are usually moving arround, this makes garbage collection possible. But when we use unsafe pointers to memory addresses, that memory must not be moved.

  • We use the fixed statement to ensure that the garbage collector does not relocate the string data.

Fixed Variables

var myStr = "Hello world!";

fixed (char* ptr = myStr)
{
    // myStr is now fixed (won't be [re]moved by the Garbage Collector).
    // We can now do something with ptr.
}

Used in an unsafe context.

Fixed Array Size

unsafe struct Example
{
    public fixed byte SomeField[8];
    public fixed char AnotherField[64];
}

fixed can only be used on fields in a struct (must also be used in an unsafe context).

default

For classes, interfaces, delegate, array, nullable (such as int?) and pointer types, default(TheType) returns null:

class MyClass {}
Debug.Assert(default(MyClass) == null);
Debug.Assert(default(string) == null);

For structs and enums, default(TheType) returns the same as new TheType():

struct Coordinates
{
    public int X { get; set; }
    public int Y { get; set; }
}

struct MyStruct
{
    public string Name { get; set; }
    public Coordinates Location { get; set; }
    public Coordinates? SecondLocation { get; set; }
    public TimeSpan Duration { get; set; }
}

var defaultStruct = default(MyStruct);
Debug.Assert(defaultStruct.Equals(new MyStruct()));
Debug.Assert(defaultStruct.Location.Equals(new Coordinates()));
Debug.Assert(defaultStruct.Location.X == 0);
Debug.Assert(defaultStruct.Location.Y == 0);
Debug.Assert(defaultStruct.SecondLocation == null);
Debug.Assert(defaultStruct.Name == null);
Debug.Assert(defaultStruct.Duration == TimeSpan.Zero);

default(T) can be particularly useful when T is a generic parameter for which no constraint is present to decide whether T is a reference type or a value type, for example:

public T GetResourceOrDefault<T>(string resourceName)
{
   if (ResourceExists(resourceName))
   {
      return (T)GetResource(resourceName);
   }
   else
   {
      return default(T);
   }
}

readonly

The readonly keyword is a field modifier. When a field declaration includes a readonly modifier, assignments to that field can only occur as part of the declaration or in a constructor in the same class.

The readonly keyword is different from the const keyword. A const field can only be initialized at the declaration of the field. A readonly field can be initialized either at the declaration or in a constructor. Therefore, readonly fields can have different values depending on the constructor used.

The readonly keyword is often used when injecting dependencies.

class Person
{
    readonly string _name;
    readonly string _surname = "Surname";

    Person(string name)
    {
        _name = name;
    }
    void ChangeName()
    {
        _name = "another name"; // Compile error
        _surname = "another surname"; // Compile error
    }
}

Note: Declaring a field readonly does not imply immutability. If the field is a reference type then the content of the object can be changed. Readonly is typically used to prevent having the object being overwritten and assigned only during instantiation of that object.

Note: Inside the constructor a readonly field can be reassigned

public class Car
{
    public double Speed {get; set;}
}

//In code

private readonly Car car = new Car();

private void SomeMethod()
{
    car.Speed = 100;
}

as

The as keyword is an operator similar to a cast. If a cast is not possible, using as produces null rather than resulting in an InvalidCastException.

expression as type is equivalent to expression is type ? (type)expression : (type)null with the caveat that as is only valid on reference conversions, nullable conversions, and boxing conversions. User-defined conversions are not supported; a regular cast must be used instead.

For the expansion above, the compiler generates code such that expression will only be evaluated once and use single dynamic type check (unlike the two in the sample above).

as can be useful when expecting an argument to facilitate several types. Specifically it grants the user multiple options - rather than checking every possibility with is before casting, or just casting and catching exceptions. It is best practice to use ‘as’ when casting/checking an object which will cause only one unboxing penalty. Using is to check, then casting will cause two unboxing penalties.

If an argument is expected to be an instance of a specific type, a regular cast is preferred as its purpose is more clear to the reader.

Because a call to as may produce null, always check the result to avoid a NullReferenceException.

Example usage

object something = "Hello";
Console.WriteLine(something as string);        //Hello
Console.Writeline(something as Nullable<int>); //null
Console.WriteLine(something as int?);          //null

//This does NOT compile:
//destination type must be a reference type (or a nullable value type)
Console.WriteLine(something as int);

Live Demo on .NET Fiddle

Equivalent example without using as:

Console.WriteLine(something is string ? (string)something : (string)null);

This is useful when overriding the Equals function in custom classes.

class MyCustomClass
{

    public override bool Equals(object obj)
    {
        MyCustomClass customObject = obj as MyCustomClass;

        // if it is null it may be really null
        // or it may be of a different type
        if (Object.ReferenceEquals(null, customObject))
        {
            // If it is null then it is not equal to this instance.
            return false;
        }

        // Other equality controls specific to class
    }

}

is

Checks if an object is compatible with a given type, i.e. if an object is an instance of the BaseInterface type, or a type that derives from BaseInterface:

interface BaseInterface {}
class BaseClass : BaseInterface {}
class DerivedClass : BaseClass {}

var d = new DerivedClass();
Console.WriteLine(d is DerivedClass);  // True
Console.WriteLine(d is BaseClass);     // True
Console.WriteLine(d is BaseInterface); // True
Console.WriteLine(d is object);        // True
Console.WriteLine(d is string);        // False

var b = new BaseClass();
Console.WriteLine(b is DerivedClass);  // False
Console.WriteLine(b is BaseClass);     // True
Console.WriteLine(b is BaseInterface); // True
Console.WriteLine(b is object);        // True
Console.WriteLine(b is string);        // False

If the intent of the cast is to use the object, it is best practice to use the as keyword’

interface BaseInterface {}
class BaseClass : BaseInterface {}
class DerivedClass : BaseClass {}

var d = new DerivedClass();
Console.WriteLine(d is DerivedClass);  // True - valid use of 'is'
Console.WriteLine(d is BaseClass);     // True - valid use of 'is'

if(d is BaseClass){
    var castedD = (BaseClass)d;
    castedD.Method(); // valid, but not best practice
}

var asD = d as BaseClass;

if(asD!=null){
    asD.Method(); //prefered method since you incur only one unboxing penalty
}

But, from C# 7 pattern matching feature extends the is operator to check for a type and declare a new variable at the same time. Same code part with C# 7 :

if(d is BaseClass asD ){
    asD.Method();
}

typeof

Returns the Type of an object, without the need to instantiate it.

Type type = typeof(string);
Console.WriteLine(type.FullName); //System.String
Console.WriteLine("Hello".GetType() == type); //True
Console.WriteLine("Hello".GetType() == typeof(string)); //True

const

const is used to represent values that will never change throughout the lifetime of the program. Its value is constant from compile-time, as opposed to the readonly keyword, whose value is constant from run-time.

For example, since the speed of light will never change, we can store it in a constant.

const double c = 299792458;  // Speed of light

double CalculateEnergy(double mass)
{
    return mass * c * c;
}

This is essentially the same as having return mass * 299792458 * 299792458, as the compiler will directly substitute c with its constant value.

As a result, c cannot be changed once declared. The following will produce a compile-time error:

const double c = 299792458;  // Speed of light 

c = 500;  //compile-time error

A constant can be prefixed with the same access modifiers as methods:

private const double c = 299792458;
public const double c = 299792458;
internal const double c = 299792458;

const members are static by nature. However using static explicitly is not permitted.

You can also define method-local constants:

double CalculateEnergy(double mass)
{
    const c = 299792458;
    return mass * c * c;
}

These can not be prefixed with a private or public keyword, since they are implicitly local to the method they are defined in.


Not all types can be used in a const declaration. The value types that are allowed, are the pre-defined types sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, and all enum types. Trying to declare const members with other value types (such as TimeSpan or Guid) will fail at compile-time.

For the special pre-defined reference type string, constants can be declared with any value. For all other reference types, constants can be declared but must always have the value null.


Because const values are known at compile-time, they are allowed as case labels in a switch statement, as standard arguments for optional parameters, as arguments to attribute specifications, and so on.


If const values are used across different assemblies, care must be taken with versioning. For example, if assembly A defines a public const int MaxRetries = 3;, and assembly B uses that constant, then if the value of MaxRetries is later changed to 5 in assembly A (which is then re-compiled), that change will not be effective in assembly B unless assembly B is also re-compiled (with a reference to the new version of A).

For that reason, if a value might change in future revisions of the program, and if the value needs to be publicly visible, do not declare that value const unless you know that all dependent assemblies will be re-compiled whenever something is changed. The alternative is using static readonly instead of const, which is resolved at runtime.

namespace

The namespace keyword is an organization construct that helps us understand how a codebase is arranged. Namespaces in C# are virtual spaces rather than being in a physical folder.

namespace StackOverflow
{
    namespace Documentation
    {
        namespace CSharp.Keywords
        {
            public class Program
            {
                public static void Main()
                {
                    Console.WriteLine(typeof(Program).Namespace);
                    //StackOverflow.Documentation.CSharp.Keywords
                }
            }
        }
    }
}

Namespaces in C# can also be written in chained syntax. The following is equivalent to above:

namespace StackOverflow.Documentation.CSharp.Keywords
{
    public class Program
    {
        public static void Main()
        {
            Console.WriteLine(typeof(Program).Namespace);
            //StackOverflow.Documentation.CSharp.Keywords
        }
    }
}

try, catch, finally, throw

try, catch, finally, and throw allow you to handle exceptions in your code.

var processor = new InputProcessor();

// The code within the try block will be executed. If an exception occurs during execution of
// this code, execution will pass to the catch block corresponding to the exception type.
try 
{
    processor.Process(input);
}
// If a FormatException is thrown during the try block, then this catch block
// will be executed.
catch (FormatException ex)
{
    // Throw is a keyword that will manually throw an exception, triggering any catch block that is
    // waiting for that exception type. 
    throw new InvalidOperationException("Invalid input", ex);
}
// catch can be used to catch all or any specific exceptions. This catch block,
// with no type specified, catches any exception that hasn't already been caught
// in a prior catch block.
catch
{
    LogUnexpectedException(); 
    throw; // Re-throws the original exception.
}
// The finally block is executed after all try-catch blocks have been; either after the try has
// succeeded in running all commands or after all exceptions have been caught. 
finally
{
    processor.Dispose();
}

Note: The return keyword can be used in try block, and the finally block will still be executed (just before returning). For example:

try 
{
    connection.Open();
    return connection.Get(query);
} 
finally 
{
    connection.Close();
}

The statement connection.Close() will execute before the result of connection.Get(query) is returned.

continue

Immediately pass control to the next iteration of the enclosing loop construct (for, foreach, do, while):

for (var i = 0; i < 10; i++)
{
    if (i < 5)
    {
        continue;
    }
    Console.WriteLine(i);
}

Output:

5
6
7
8
9

Live Demo on .NET Fiddle

var stuff = new [] {"a", "b", null, "c", "d"};

foreach (var s in stuff)
{
    if (s == null)
    {
        continue;
    }           
    Console.WriteLine(s);
}

Output:

a
b
c
d

Live Demo on .NET Fiddle

ref, out

The ref and out keywords cause an argument to be passed by reference, not by value. For value types, this means that the value of the variable can be changed by the callee.

int x = 5;
ChangeX(ref x);
// The value of x could be different now

For reference types, the instance in the variable can not only be modified (as is the case without ref), but it can also be replaced altogether:

Address a = new Address();
ChangeFieldInAddress(a);
// a will be the same instance as before, even if it is modified
CreateANewInstance(ref a);
// a could be an entirely new instance now

The main difference between the out and ref keyword is that ref requires the variable to be initialized by the caller, while out passes that responsibility to the callee.

To use an out parameter, both the method definition and the calling method must explicitly use the out keyword.

int number = 1;
Console.WriteLine("Before AddByRef: " + number); // number = 1
AddOneByRef(ref number);
Console.WriteLine("After AddByRef: " + number);  // number = 2
SetByOut(out number);
Console.WriteLine("After SetByOut: " + number);  // number = 34

void AddOneByRef(ref int value)
{
    value++;
}

void SetByOut(out int value)
{
    value = 34;
}

Live Demo on .NET Fiddle

The following does not compile, because out parameters must have a value assigned before the method returns (it would compile using ref instead):

void PrintByOut(out int value)
{
    Console.WriteLine("Hello!");
}

using out keyword as Generic Modifier

out keyword can also be used in generic type parameters when defining generic interfaces and delegates. In this case, the out keyword specifies that the type parameter is covariant.

Covariance enables you to use a more derived type than that specified by the generic parameter. This allows for implicit conversion of classes that implement variant interfaces and implicit conversion of delegate types. Covariance and contravariance are supported for reference types, but they are not supported for value types. - MSDN

//if we have an interface like this
interface ICovariant<out R> { }

//and two variables like
ICovariant<Object> iobj = new Sample<Object>();
ICovariant<String> istr = new Sample<String>();

// then the following statement is valid
// without the out keyword this would have thrown error
iobj = istr; // implicit conversion occurs here

checked, unchecked

The checked and unchecked keywords define how operations handle mathematical overflow. “Overflow” in the context of the checked and unchecked keywords is when an integer arithmetic operation results in a value which is greater in magnitude than the target data type can represent.

When overflow occurs within a checked block (or when the compiler is set to globally use checked arithmetic), an exception is thrown to warn of undesired behavior. Meanwhile, in an unchecked block, overflow is silent: no exceptions are thrown, and the value will simply wrap around to the opposite boundary. This can lead to subtle, hard to find bugs.

Since most arithmetic operations are done on values that are not large or small enough to overflow, most of the time, there is no need to explicitly define a block as checked. Care needs to be taken when doing arithmetic on unbounded input that may cause overflow, for example when doing arithmetic in recursive functions or while taking user input.

Neither checked nor unchecked affect floating point arithmetic operations.

When a block or expression is declared as unchecked, any arithmetic operations inside it are allowed to overflow without causing an error. An example where this behavior is desired would be the calculation of a checksum, where the value is allowed to “wrap around” during calculation:

byte Checksum(byte[] data) {
    byte result = 0;
    for (int i = 0; i < data.Length; i++) {
        result = unchecked(result + data[i]); // unchecked expression
    }
    return result;
}

One of the most common uses for unchecked is implementing a custom override for object.GetHashCode(), a type of checksum. You can see the keyword’s use in the answers to this question: https://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode.

When a block or expression is declared as checked, any arithmetic operation that causes an overflow results in an OverflowException being thrown.

int SafeSum(int x, int y) {
    checked { // checked block
        return x + y; 
    }
}

Both checked and unchecked may be in block and expression form.

Checked and unchecked blocks do not affect called methods, only operators called directly in the current method. For example, Enum.ToObject(), Convert.ToInt32(), and user-defined operators are not affected by custom checked/unchecked contexts.

Note: The default overflow default behavior (checked vs. unchecked) may be changed in the Project Properties or through the /checked[+|-] command line switch. It is common to default to checked operations for debug builds and unchecked for release builds. The checked and unchecked keywords would then be used only where a default approach does not apply and you need an explicit behavior to ensure correctness.

goto

goto can be used to jump to a specific line inside the code, specified by a label.

goto as a:

Label:

void InfiniteHello()
{
    sayHello:
    Console.WriteLine("Hello!");
    goto sayHello;
}

Live Demo on .NET Fiddle

Case statement:

enum Permissions { Read, Write };

switch (GetRequestedPermission())
{
    case Permissions.Read:
        GrantReadAccess();
        break;

    case Permissions.Write:
        GrantWriteAccess();
        goto case Permissions.Read; //People with write access also get read
}

Live Demo on .NET Fiddle

This is particularly useful in executing multiple behaviors in a switch statement, as C# does not support fall-through case blocks.

Exception Retry

var exCount = 0;
retry:
try
{
    //Do work
}
catch (IOException)
{
    exCount++;
    if (exCount < 3)
    {
        Thread.Sleep(100);
        goto retry;
    }
    throw;
}

Live Demo on .NET Fiddle

Similar to many languages, use of goto keyword is discouraged except the cases below.

Valid usages of goto which apply to C#:

  • Fall-through case in switch statement.

  • Multi-level break. LINQ can often be used instead, but it usually has worse performance.

  • Resource deallocation when working with unwrapped low-level objects. In C#, low-level objects should usually be wrapped in separate classes.

  • Finite state machines, for example, parsers; used internally by compiler generated async/await state machines.

enum

The enum keyword tells the compiler that this class inherits from the abstract class Enum, without the programmer having to explicitly inherit it. Enum is a descendant of ValueType, which is intended for use with distinct set of named constants.

public enum DaysOfWeek
{
    Monday,
    Tuesday,
}

You can optionally specify a specific value for each one (or some of them):

public enum NotableYear
{
   EndOfWwI = 1918;
   EnfOfWwII = 1945,
}

In this example I omitted a value for 0, this is usually a bad practice. An enum will always have a default value produced by explicit conversion (YourEnumType) 0, where YourEnumType is your declared enume type. Without a value of 0 defined, an enum will not have a defined value at initiation.

The default underlying type of enum is int, you can change the underlying type to any integral type including byte, sbyte, short, ushort, int, uint, long and ulong. Below is an enum with underlying type byte:

enum Days : byte
{
    Sunday = 0,
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday
};

Also note that you can convert to/from underlying type simply with a cast:

int value = (int)NotableYear.EndOfWwI;

For these reasons you’d better always check if an enum is valid when you’re exposing library functions:

void PrintNotes(NotableYear year)
{
    if (!Enum.IsDefined(typeof(NotableYear), year))
        throw InvalidEnumArgumentException("year", (int)year, typeof(NotableYear));

    // ...
}

base

The base keyword is used to access members from a base class. It is commonly used to call base implementations of virtual methods, or to specify which base constructor should be called.

Choosing a constructor

public class Child : SomeBaseClass {
    public Child() : base("some string for the base class")
    {
    }
}

public class SomeBaseClass {
    public SomeBaseClass()
    {
        // new Child() will not call this constructor, as it does not have a parameter
    }
    public SomeBaseClass(string message)
    {
        // new Child() will use this base constructor because of the specified parameter in Child's constructor
        Console.WriteLine(message);
    }
}

Calling base implementation of virtual method

public override void SomeVirtualMethod() {
    // Do something, then call base implementation
    base.SomeVirtualMethod();
}

It is possible to use the base keyword to call a base implementation from any method. This ties the method call directly to the base implementation, which means that even if new child classes override a virtual method, the base implementation will still be called so this needs to be used with caution.

public class Parent
{
    public virtual int VirtualMethod()
    {
        return 1;
    }
}

public class Child : Parent
{
    public override int VirtualMethod() {
        return 11;
    }

    public int NormalMethod()
    {
        return base.VirtualMethod();
    }

    public void CallMethods()
    {
        Assert.AreEqual(11, VirtualMethod());

        Assert.AreEqual(1, NormalMethod());
        Assert.AreEqual(1, base.VirtualMethod());
    }
}

public class GrandChild : Child
{
    public override int VirtualMethod()
    {
        return 21;
    }

    public void CallAgain()
    {
        Assert.AreEqual(21, VirtualMethod());
        Assert.AreEqual(11, base.VirtualMethod());

        // Notice that the call to NormalMethod below still returns the value
        // from the extreme base class even though the method has been overridden
        // in the child class.
        Assert.AreEqual(1, NormalMethod());
    }
}

foreach

foreach is used to iterate over the elements of an array or the items within a collection which implements IEnumerable✝.

var lines = new string[] { 
    "Hello world!", 
    "How are you doing today?", 
    "Goodbye"
};

foreach (string line in lines)
{
    Console.WriteLine(line);
}

This will output

“Hello world!”
“How are you doing today?”
“Goodbye”

Live Demo on .NET Fiddle

You can exit the foreach loop at any point by using the break keyword or move on to the next iteration using the continue keyword.

var numbers = new int[] {1, 2, 3, 4, 5, 6};

foreach (var number in numbers)
{
    // Skip if 2
    if (number == 2)
        continue;

    // Stop iteration if 5
    if (number == 5)
        break;

    Console.Write(number + ", ");
}

// Prints: 1, 3, 4, 

Live Demo on .NET Fiddle

Notice that the order of iteration is guaranteed only for certain collections such as arrays and List, but not guaranteed for many other collections.


✝ While IEnumerable is typically used to indicate enumerable collections, foreach only requires that the collection expose publicly the object GetEnumerator() method, which should return an object that exposes the bool MoveNext() method and the object Current { get; } property.

params

params allows a method parameter to receive a variable number of arguments, i.e. zero, one or multiple arguments are allowed for that parameter.

static int AddAll(params int[] numbers)
{
    int total = 0;
    foreach (int number in numbers)
    {
        total += number;
    }
    
    return total;
}

This method can now be called with a typical list of int arguments, or an array of ints.

AddAll(5, 10, 15, 20);                // 50
AddAll(new int[] { 5, 10, 15, 20 });  // 50

params must appear at most once and if used, it must be last in the argument list, even if the succeeding type is different to that of the array.


Be careful when overloading functions when using the params keyword. C# prefers matching more specific overloads before resorting to trying to use overloads with params. For example if you have two methods:

static double Add(params double[] numbers)
{
    Console.WriteLine("Add with array of doubles");
    double total = 0.0;
    foreach (double number in numbers)
    {
        total += number;
    }
    
    return total;
}

static int Add(int a, int b)
{
    Console.WriteLine("Add with 2 ints");
    return a + b;
}

Then the specific 2 argument overload will take precedence before trying the params overload.

Add(2, 3);      //prints "Add with 2 ints"
Add(2, 3.0);    //prints "Add with array of doubles" (doubles are not ints)
Add(2, 3, 4);   //prints "Add with array of doubles" (no 3 argument overload)

break

In a loop (for, foreach, do, while) the break statement aborts the execution of the innermost loop and returns to the code after it. Also it can be used with yield in which it specifies that an iterator has come to an end.

for (var i = 0; i < 10; i++)
{
    if (i == 5)
    {
        break;
    }
    Console.WriteLine("This will appear only 5 times, as the break will stop the loop.");
}

Live Demo on .NET Fiddle

foreach (var stuff in stuffCollection)
{
    if (stuff.SomeStringProp == null)
        break;
    // If stuff.SomeStringProp for any "stuff" is null, the loop is aborted.
    Console.WriteLine(stuff.SomeStringProp);
}

The break-statement is also used in switch-case constructs to break out of a case or default segment.

switch(a)
{
    case 5:
        Console.WriteLine("a was 5!");
        break;

    default:
        Console.WriteLine("a was something else!");
        break;
}

In switch statements, the ‘break’ keyword is required at the end of each case statement. This is contrary to some languages that allow for ‘falling through’ to the next case statement in the series. Workarounds for this would include ‘goto’ statements or stacking the ‘case’ statements sequentially.

Following code will give numbers 0, 1, 2, ..., 9 and the last line will not be executed. yield break signifies the end of the function (not just a loop).

public static IEnumerable<int> GetNumbers()
{
    int i = 0;
    while (true) {
        if (i < 10) {
            yield return i++;
        } else {
            yield break;
        }
    }
    Console.WriteLine("This line will not be executed");
}

Live Demo on .NET Fiddle

Note that unlike some other languages, there is no way to label a particular break in C#. This means that in the case of nested loops, only the innermost loop will be stopped:

foreach (var outerItem in outerList)
{
    foreach (var innerItem in innerList)
    {
        if (innerItem.ShoudBreakForWhateverReason)
            // This will only break out of the inner loop, the outer will continue:
            break; 
    }
}

If you want to break out of the outer loop here, you can use one of several different strategies, such as:

  • A goto statement to jump out of the whole looping structure.

  • A specific flag variable (shouldBreak in the following example) that can be checked at the end of each iteration of the outer loop.

  • Refactoring the code to use a return statement in the innermost loop body, or avoid the whole nested loop structure altogether.

    bool shouldBreak = false; while(comeCondition) { while(otherCondition) { if (conditionToBreak) { // Either tranfer control flow to the label below… goto endAllLooping;

             // OR use a flag, which can be checked in the outer loop:
             shouldBreak = true;
         }
     }
    
     if(shouldBreakNow)
     {
         break; // Break out of outer loop if flag was set to true
     }

    }

    endAllLooping: // label from where control flow will continue

abstract

A class marked with the keyword abstract cannot be instantiated.

A class must be marked as abstract if it contains abstract members or if it inherits abstract members that it doesn’t implement. A class may be marked as abstract even if no abstract members are involved.

Abstract classes are usually used as base classes when some part of the implementation needs to be specified by another component.

abstract class Animal 
{
    string Name { get; set; }
    public abstract void MakeSound();
}

public class Cat : Animal 
{
    public override void MakeSound()
    {
        Console.WriteLine("Meov meov");
    }
}

public class Dog : Animal 
{   
    public override void MakeSound()
    {
        Console.WriteLine("Bark bark");
    }
}

Animal cat = new Cat();       // Allowed due to Cat deriving from Animal
cat.MakeSound();              // will print out "Meov meov"    

Animal dog = new Dog();       // Allowed due to Dog deriving from Animal
dog.MakeSound();              // will print out "Bark bark"

Animal animal = new Animal(); // Not allowed due to being an abstract class

A method, property, or event marked with the keyword abstract indicates that the implementation for that member is expected to be provided in a subclass. As mentioned above, abstract members can only appear in abstract classes.

abstract class Animal 
{
   public abstract string Name { get; set; }
}

public class Cat : Animal 
{
    public override string Name { get; set; }
}

public class Dog : Animal 
{
    public override string Name { get; set; }
}

float, double, decimal

float

float is an alias to the .NET datatype System.Single. It allows IEEE 754 single-precision floating point numbers to be stored. This data type is present in mscorlib.dll which is implicitly referenced by every C# project when you create them.

Approximate range: -3.4 × 1038 to 3.4 × 1038

Decimal precision: 6-9 significant digits

Notation:

float f = 0.1259;
var f1 = 0.7895f; // f is literal suffix to represent float values 

It should be noted that the float type often results in significant rounding errors. In applications where precision is important, other data types should be considered.


double

double is an alias to the .NET datatype System.Double. It represents a double-precision 64-bit floating-point number. This datatype is present in mscorlib.dll which is implicitly referenced in any C# project.

Range: ±5.0 × 10−324 to ±1.7 × 10308

Decimal precision: 15-16 significant digits

Notation:

double distance = 200.34; // a double value
double salary = 245; // an integer implicitly type-casted to double value
var marks = 123.764D; // D is literal suffix to represent double values

decimal

decimal is an alias to the .NET datatype System.Decimal. It represents a keyword indicates a 128-bit data type. Compared to floating-point types, the decimal type has more precision and a smaller range, which makes it appropriate for financial and monetary calculations. This datatype is present in mscorlib.dll which is implicitly referenced in any C# project.

Range: -7.9 × 1028 to 7.9 × 1028

Decimal precision: 28-29 significant digits

Notation:

decimal payable = 152.25m; // a decimal value
var marks = 754.24m; // m is literal suffix to represent decimal values

uint

An unsigned integer, or uint, is a numeric datatype that only can hold positive integers. Like it’s name suggests, it represents an unsigned 32-bit integer. The uint keyword itself is an alias for the Common Type System type System.UInt32. This datatype is present in mscorlib.dll, which is implicitly referenced by every C# project when you create them. It occupies four bytes of memory space.

Unsigned integers can hold any value from 0 to 4,294,967,295.

Examples on how and now not to declare unsigned integers

uint i = 425697; // Valid expression, explicitly stated to compiler
var i1 = 789247U; // Valid expression, suffix allows compiler to determine datatype
uint x = 3.0; // Error, there is no implicit conversion

Please note: According to Microsoft, it is recommended to use the int datatype wherever possible as the uint datatype is not CLS-compliant.

this

The this keyword refers to the current instance of class(object). That way two variables with the same name, one at the class-level (a field) and one being a parameter (or local variable) of a method, can be distinguished.

public MyClass {
    int a;

    void set_a(int a)
    {
        //this.a refers to the variable defined outside of the method,
        //while a refers to the passed parameter.
        this.a = a;
    }
}

Other usages of the keyword are chaining non-static constructor overloads:

public MyClass(int arg) : this(arg, null)
{
}

and writing indexers:

public string this[int idx1, string idx2]
{
    get { /* ... */ }
    set { /* ... */ }
}

and declaring extension methods:

public static int Count<TItem>(this IEnumerable<TItem> source)
{
    // ...
}

If there is no conflict with a local variable or parameter, it is a matter of style whether to use this or not, so this.MemberOfType and MemberOfType would be equivalent in that case. Also see base keyword.

Note that if an extension method is to be called on the current instance, this is required. For example if your are inside a non-static method of a class which implements IEnumerable<> and you want to call the extension Count from before, you must use:

this.Count()  // works like StaticClassForExtensionMethod.Count(this)

and this cannot be omitted there.

for

Syntax: for (initializer; condition; iterator)

  • The for loop is commonly used when the number of iterations is known.
  • The statements in the initializer section run only once, before you enter the loop.
  • The condition section contains a boolean expression that’s evaluated at the end of every loop iteration to determine whether the loop should exit or should run again.
  • The iterator section defines what happens after each iteration of the body of the loop.

This example shows how for can be used to iterate over the characters of a string:

string str = "Hello";
for (int i = 0; i < str.Length; i++)
{
    Console.WriteLine(str[i]);                
}
                 

Output:

H
e
l
l
o

Live Demo on .NET Fiddle

All of the expressions that define a for statement are optional; for example, the following statement is used to create an infinite loop:

for( ; ; )
{
    // Your code here
}

The initializer section can contain multiple variables, so long as they are of the same type. The condition section can consist of any expression which can be evaluated to a bool. And the iterator section can perform multiple actions separated by comma:

string hello = "hello";
for (int i = 0, j = 1, k = 9; i < 3 && k > 0; i++, hello += i) {
    Console.WriteLine(hello);
}

Output:

hello
hello1
hello12

Live Demo on .NET Fiddle

while

The while operator iterates over a block of code until the conditional query equals false or the code is interrupted with a goto, return, break or throw statement.

Syntax for while keyword:

while( condition ) { code block; }

Example:

int i = 0;
while (i++ < 5)
{
    Console.WriteLine("While is on loop number {0}.", i);
}

Output:

“While is on loop number 1.” \

“While is on loop number 2.”
“While is on loop number 3.”
“While is on loop number 4.”
“While is on loop number 5.”

Live Demo on .NET Fiddle

A while loop is Entry Controlled, as the condition is checked before the execution of the enclosed code block. This means that the while loop wouldn’t execute its statements if the condition is false.

bool a = false;

while (a == true)
{
    Console.WriteLine("This will never be printed.");
}

Giving a while condition without provisioning it to become false at some point will result in an infinite or endless loop. As far as possible, this should be avoided, however, there may be some exceptional circumstances when you need this.

You can create such a loop as follows:

while (true)
{
//...
}

Note that the C# compiler will transform loops such as

while (true)
{
// ...
}

or

for(;;)
{
// ...
}

into

{
:label
// ...
goto label;
}

Note that a while loop may have any condition, no matter how complex, as long as it evaluates to (or returns) a boolean value (bool). It may also contain a function that returns a boolean value (as such a function evaluates to the same type as an expression such as `a==x’). For example,

while (AgriculturalService.MoreCornToPick(myFarm.GetAddress()))
{
    myFarm.PickCorn();
}

return

MSDN: The return statement terminates execution of the method in which it appears and returns control to the calling method. It can also return an optional value. If the method is a void type, the return statement can be omitted.

public int Sum(int valueA, int valueB)
{
    return valueA + valueB;
}


public void Terminate(bool terminateEarly)
{
    if (terminateEarly) return; // method returns to caller if true was passed in
    else Console.WriteLine("Not early"); // prints only if terminateEarly was false
}

in

The in keyword has three uses:

a) As part of the syntax in a foreach statement or as part of the syntax in a LINQ query

foreach (var member in sequence)
{
    // ...
}

b) In the context of generic interfaces and generic delegate types signifies contravariance for the type parameter in question:

public interface IComparer<in T>
{
    // ...
}

c) In the context of LINQ query refers to the collection that is being queried

var query = from x in source select new { x.Name, x.ID, };

using

There are two types of using keyword usage, using statement and using directive:

  1. using statement:

    The using keyword ensures that objects that implement the IDisposable interface are properly disposed after usage. There is a separate topic for the using statement

  2. using directive

    The using directive has three usages, see the msdn page for the using directive. There is a separate topic for the using directive.

sealed

When applied to a class, the sealed modifier prevents other classes from inheriting from it.

class A { }
sealed class B : A { }
class C : B { } //error : Cannot derive from the sealed class

When applied to a virtual method (or virtual property), the sealed modifier prevents this method (property) from being overriden in derived classes.

public class A 
{
    public sealed override string ToString() // Virtual method inherited from class Object
    {
        return "Do not override me!";
    }
}

public class B: A 
{
    public override string ToString() // Compile time error
    { 
        return "An attempt to override"; 
    }
}

sizeof

Used to obtain the size in bytes for an unmanaged type

int byteSize = sizeof(byte) // 1
int sbyteSize = sizeof(sbyte) // 1
int shortSize = sizeof(short) // 2
int ushortSize = sizeof(ushort) // 2
int intSize = sizeof(int) // 4
int uintSize = sizeof(uint) // 4
int longSize = sizeof(long) // 8
int ulongSize = sizeof(ulong) // 8
int charSize = sizeof(char) // 2(Unicode)
int floatSize = sizeof(float) // 4
int doubleSize = sizeof(double) // 8
int decimalSize = sizeof(decimal) // 16
int boolSize = sizeof(bool) // 1

static

The static modifier is used to declare a static member, which does not need to be instantiated in order to be accessed, but instead is accessed simply through its name, i.e. DateTime.Now.

static can be used with classes, fields, methods, properties, operators, events, and constructors.

While an instance of a class contains a separate copy of all instance fields of the class, there is only one copy of each static field.

class A
{
    static public int count = 0;

    public A()
    {
        count++;
    }
}

class Program
{
    static void Main(string[] args)
    {
        A a = new A();
        A b = new A();
        A c = new A();

        Console.WriteLine(A.count); // 3 
    }
}

count equals to the total number of instances of A class.

The static modifier can also be used to declare a static constructor for a class, to initialize static data or run code that only needs to be called once. Static constructors are called before the class is referenced for the first time.

class A
{
    static public DateTime InitializationTime;

    // Static constructor
    static A()
    {
        InitializationTime = DateTime.Now;
        // Guaranteed to only run once
        Console.WriteLine(InitializationTime.ToString());
    }
}

A static class is marked with the static keyword, and can be used as a beneficial container for a set of methods that work on parameters, but don’t necessarily require being tied to an instance. Because of the static nature of the class, it cannot be instantiated, but it can contain a static constructor. Some features of a static class include:

  • Can’t be inherited
  • Can’t inherit from anything other than Object
  • Can contain a static constructor but not an instance constructor
  • Can only contain static members
  • Is sealed

The compiler is also friendly and will let the developer know if any instance members exist within the class. An example would be a static class that converts between US and Canadian metrics:

static class ConversionHelper {
    private static double oneGallonPerLitreRate = 0.264172;

    public static double litreToGallonConversion(int litres) {
        return litres * oneGallonPerLitreRate;
    }
}

When classes are declared static:

public static class Functions
{
  public static int Double(int value)
  {
    return value + value;
  }
}

all function, properties or members within the class also need to be declared static. No instance of the class can be created. In essence a static class allows you to create bundles of functions that are grouped together logically.

Since C#6 static can also be used alongside using to import static members and methods. They can be used then without class name.

Old way, without using static:

using System;

public class ConsoleApplication
{
    public static void Main()
    {
         Console.WriteLine("Hello World!"); //Writeline is method belonging to static class Console
    }

}

Example with using static

using static System.Console;

public class ConsoleApplication
{
    public static void Main()
    {
         WriteLine("Hello World!"); //Writeline is method belonging to static class Console
    }

}

Drawbacks

While static classes can be incredibly useful, they do come with their own caveats:

• Once the static class has been called, the class is loaded into memory and cannot be run through the garbage collector until the AppDomain housing the static class is unloaded.

• A static class cannot implement an interface.

int

int is an alias for System.Int32, which is a data type for signed 32-bit integers. This data type can be found in mscorlib.dll which is implicitly referenced by every C# project when you create them.

Range: -2,147,483,648 to 2,147,483,647

int int1 = -10007;
var int2 = 2132012521;     

long

The long keyword is used to represent signed 64-bit integers. It is an alias for the System.Int64 datatype present in mscorlib.dll, which is implicitly referenced by every C# project when you create them.

Any long variable can be declared both explicitly and implicitly:

long long1 = 9223372036854775806;  // explicit declaration, long keyword used
var long2 = -9223372036854775806L; // implicit declaration, 'L' suffix used

A long variable can hold any value from –9,223,372,036,854,775,808 to 9,223,372,036,854,775,807, and can be useful in situations which a variable must hold a value that exceeds the bounds of what other variables (such as the int variable) can hold.

ulong

Keyword used for unsigned 64-bit integers. It represents System.UInt64 data type found in mscorlib.dll which is implicitly referenced by every C# project when you create them.

Range: 0 to 18,446,744,073,709,551,615

ulong veryLargeInt = 18446744073609451315;
var anotherVeryLargeInt = 15446744063609451315UL;

dynamic

The dynamic keyword is used with dynamically typed objects. Objects declared as dynamic forego compile-time static checks, and are instead evaluated at runtime.

using System;
using System.Dynamic;

dynamic info = new ExpandoObject();
info.Id = 123;
info.Another = 456;

Console.WriteLine(info.Another);
// 456

Console.WriteLine(info.DoesntExist);
// Throws RuntimeBinderException

The following example uses dynamic with Newtonsoft’s library Json.NET, in order to easily read data from a deserialized JSON file.

try
{
    string json = @"{ x : 10, y : ""ho""}";
    dynamic deserializedJson = JsonConvert.DeserializeObject(json);
    int x = deserializedJson.x;
    string y = deserializedJson.y;
    // int z = deserializedJson.z; // throws RuntimeBinderException
}
catch (RuntimeBinderException e)
{
    // This exception is thrown when a property
    // that wasn't assigned to a dynamic variable is used
}

There are some limitations associated with the dynamic keyword. One of them is the use of extension methods. The following example adds an extension method for string: SayHello.

static class StringExtensions
{
    public static string SayHello(this string s) => $"Hello {s}!";
}

The first approach will be to call it as usual (as for a string):

var person = "Person";
Console.WriteLine(person.SayHello());

dynamic manager = "Manager";
Console.WriteLine(manager.SayHello()); // RuntimeBinderException

No compilation error, but at runtime you get a RuntimeBinderException. The workaround for this will be to call the extension method via the static class:

var helloManager = StringExtensions.SayHello(manager);
Console.WriteLine(helloManager);

virtual, override, new

virtual and override

The virtual keyword allows a method, property, indexer or event to be overridden by derived classes and present polymorphic behavior. (Members are non-virtual by default in C#)

public class BaseClass
{
    public virtual void Foo()
    {
        Console.WriteLine("Foo from BaseClass");
    }
}

In order to override a member, the override keyword is used in the derived classes. (Note the signature of the members must be identical)

public class DerivedClass: BaseClass
{
    public override void Foo()
    {
        Console.WriteLine("Foo from DerivedClass");
    }
}

The polymorphic behavior of virtual members means that when invoked, the actual member being executed is determined at runtime instead of at compile time. The overriding member in the most derived class the particular object is an instance of will be the one executed.

In short, object can be declared of type BaseClass at compile time but if at runtime it is an instance of DerivedClass then the overridden member will be executed:

BaseClass obj1 = new BaseClass();
obj1.Foo(); //Outputs "Foo from BaseClass"

obj1 = new DerivedClass();
obj1.Foo(); //Outputs "Foo from DerivedClass"    

Overriding a method is optional:

public class SecondDerivedClass: DerivedClass {}

var obj1 = new SecondDerivedClass();
obj1.Foo(); //Outputs "Foo from DerivedClass"    

new

Since only members defined as virtual are overridable and polymorphic, a derived class redefining a non virtual member might lead to unexpected results.

public class BaseClass
{
    public void Foo()
    {
        Console.WriteLine("Foo from BaseClass");
    }
}

public class DerivedClass: BaseClass
{
    public void Foo()
    {
        Console.WriteLine("Foo from DerivedClass");
    }
}

BaseClass obj1 = new BaseClass();
obj1.Foo(); //Outputs "Foo from BaseClass"

obj1 = new DerivedClass();
obj1.Foo(); //Outputs "Foo from BaseClass" too!    

When this happens, the member executed is always determined at compile time based on the type of the object.

  • If the object is declared of type BaseClass (even if at runtime is of a derived class) then the method of BaseClass is executed
  • If the object is declared of type DerivedClass then the method of DerivedClass is executed.

This is usually an accident (When a member is added to the base type after an identical one was added to the derived type) and a compiler warning CS0108 is generated in those scenarios.

If it was intentional, then the new keyword is used to suppress the compiler warning (And inform other developers of your intentions!). the behavior remains the same, the new keyword just suppresses the compiler warning.

public class BaseClass
{
    public void Foo()
    {
        Console.WriteLine("Foo from BaseClass");
    }
}

public class DerivedClass: BaseClass
{
    public new void Foo()
    {
        Console.WriteLine("Foo from DerivedClass");
    }
}

BaseClass obj1 = new BaseClass();
obj1.Foo(); //Outputs "Foo from BaseClass"

obj1 = new DerivedClass();
obj1.Foo(); //Outputs "Foo from BaseClass" too! 

The usage of override is not optional

Unlike in C++, the usage of the override keyword is not optional:

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

public class B : A
{
    public void Foo() // Generates CS0108
    {
    }
}

The above example also causes warning CS0108, because B.Foo() is not automatically overriding A.Foo(). Add override when the intention is to override the base class and cause polymorphic behavior, add new when you want non-polymorphic behavior and resolve the call using the static type. The latter should be used with caution, as it may cause severe confusion.

The following code even results in an error:

public class A
{
    public void Foo()
    {
    }
}

public class B : A
{
    public override void Foo() // Error: Nothing to override
    {
    }
}

Derived classes can introduce polymorphism

The following code is perfectly valid (although rare):

    public class A
    {
        public void Foo()
        {
            Console.WriteLine("A");
        }
    }

    public class B : A
    {
        public new virtual void Foo() 
        {
            Console.WriteLine("B");
        }
    }

Now all objects with a static reference of B (and its derivatives) use polymorphism to resolve Foo(), while references of A use A.Foo().

A a = new A();
a.Foo(); // Prints "A";
a = new B();
a.Foo(); // Prints "A";
B b = new B();
b.Foo(); // Prints "B";

Virtual methods cannot be private

The C# compiler is strict in preventing senseless constructs. Methods marked as virtual cannot be private. Because a private method cannot be seen from a derived type, it couldn’t be overwritten either. This fails to compile:

public class A
{
    private virtual void Foo() // Error: virtual methods cannot be private
    {
    }
}

async, await

The await keyword was added as part of C# 5.0 release which is supported from Visual Studio 2012 onwards. It leverages Task Parallel Library (TPL) which made the multi-threading relatively easier. The async and await keywords are used in pair in the same function as shown below. The await keyword is used to pause the current asynchronous method’s execution until the awaited asynchronous task is completed and/or its results returned. In order to use the await keyword, the method that uses it must be marked with the async keyword.

Using async with void is strongly discouraged. For more info you can look here.

Example:

public async Task DoSomethingAsync()
{    
    Console.WriteLine("Starting a useless process...");
    Stopwatch stopwatch = Stopwatch.StartNew();
    int delay = await UselessProcessAsync(1000);
    stopwatch.Stop();
    Console.WriteLine("A useless process took {0} milliseconds to execute.", stopwatch.ElapsedMilliseconds);
}

public async Task<int> UselessProcessAsync(int x)
{
    await Task.Delay(x);
    return x;
}

Output:

“Starting a useless process…”

**… 1 second delay… **

“A useless process took 1000 milliseconds to execute.”

The keyword pairs async and await can be omitted if a Task or Task<T> returning method only returns a single asynchronous operation.

Rather than this:

public async Task PrintAndDelayAsync(string message, int delay)
{
    Debug.WriteLine(message);
    await Task.Delay(x);
}

It is preferred to do this:

public Task PrintAndDelayAsync(string message, int delay)
{
    Debug.WriteLine(message);
    return Task.Delay(x);
}

In C# 5.0 await cannot be used in catch and finally.

With C# 6.0 await can be used in catch and finally.

char

A char is single letter stored inside a variable. It is built-in value type which takes two bytes of memory space. It represents System.Char data type found in mscorlib.dll which is implicitly referenced by every C# project when you create them.

There are multiple ways to do this.

  1. char c = 'c';
  2. char c = '\u0063'; //Unicode
  3. char c = '\x0063'; //Hex
  4. char c = (char)99;//Integral

A char can be implicitly converted to ushort, int, uint, long, ulong, float, double, or decimal and it will return the integer value of that char.

ushort u = c;

returns 99 etc.

However, there are no implicit conversions from other types to char. Instead you must cast them.

ushort u = 99;
 char c = (char)u;

lock

lock provides thread-safety for a block of code, so that it can be accessed by only one thread within the same process. Example:

private static object _lockObj = new object();
static void Main(string[] args)
{
    Task.Run(() => TaskWork());
    Task.Run(() => TaskWork());
    Task.Run(() => TaskWork());

    Console.ReadKey();
}

private static void TaskWork()
{
    lock(_lockObj)
    {
        Console.WriteLine("Entered");

        Task.Delay(3000);
        Console.WriteLine("Done Delaying");

        // Access shared resources safely

        Console.WriteLine("Leaving");
    }   
}

Output:

Entered
Done Delaying
Leaving
Entered
Done Delaying
Leaving
Entered
Done Delaying
Leaving

Use cases:

Whenever you have a block of code that might produce side-effects if executed by multiple threads at the same time. The lock keyword along with a shared synchronization object (_objLock in the example) can be used to prevent that.

Note that _objLock can’t be null and multiple threads executing the code must use the same object instance (either by making it a static field, or by using the same class instance for both threads)

From the compiler side, the lock keyword is a syntactic sugar that is replaced by Monitor.Enter(_lockObj); and Monitor.Exit(_lockObj);. So if you replace the lock by surrounding the block of code with these two methods, you would get the same results. You can see actual code in Syntactic sugar in C# - lock example

null

A variable of a reference type can hold either a valid reference to an instance or a null reference. The null reference is the default value of reference type variables, as well as nullable value types.

null is the keyword that represents a null reference.

As an expression, it can be used to assign the null reference to variables of the aforementioned types:

object a = null;
string b = null;
int? c = null;
List<int> d  = null;

Non-nullable value types cannot be assigned a null reference. All the following assignments are invalid:

int a = null; 
float b = null;
decimal c = null;

The null reference should not be confused with valid instances of various types such as:

  • an empty list (new List<int>())
  • an empty string ("")
  • the number zero (0, 0f, 0m)
  • the null character ( '\0' )

Sometimes, it is meaningful to check if something is either null or an empty/default object. The System.String.IsNullOrEmpty(String) method may be used to check this, or you may implement your own equivalent method.

private void GreetUser(string userName)
{
    if (String.IsNullOrEmpty(userName))
    {
        //The method that called us either sent in an empty string, or they sent us a null reference. Either way, we need to report the problem.
        throw new InvalidOperationException("userName may not be null or empty.");
    }
    else
    {
        //userName is acceptable.
        Console.WriteLine("Hello, " + userName + "!");
    }
}

internal

The internal keyword is an access modifier for types and type members. Internal types or members are accessible only within files in the same assembly

usage:

public class BaseClass 
{
    // Only accessible within the same assembly
    internal static int x = 0;
}

The difference between different access modifiers is clarified here

Access modifiers

public

The type or member can be accessed by any other code in the same assembly or another assembly that references it.

private

The type or member can only be accessed by code in the same class or struct.

protected

The type or member can only be accessed by code in the same class or struct, or in a derived class.

internal

The type or member can be accessed by any code in the same assembly, but not from another assembly.

protected internal

The type or member can be accessed by any code in the same assembly, or by any derived class in another assembly.

When no access modifier is set, a default access modifier is used. So there is always some form of access modifier even if it’s not set.

where

where can serve two purposes in C#: type constraining in a generic argument, and filtering LINQ queries.

In a generic class, let’s consider

public class Cup<T>
{
    // ...
}

T is called a type parameter. The class definition can impose constraints on the actual types that can be supplied for T.

The following kinds of constraints can be applied:

  • value type
  • reference type
  • default constructor
  • inheritance and implementation

value type

In this case only structs (this includes ‘primitive’ data types such as int, boolean etc) can be supplied

public class Cup<T> where T : struct
{
    // ...
}

reference type

In this case only class types can be supplied

public class Cup<T> where T : class
{
    // ...
}

hybrid value/reference type

Occasionally it is desired to restrict type arguments to those available in a database, and these will usually map to value types and strings. As all type restrictions must be met, it is not possible to specify where T : struct or string (this is not valid syntax). A workaround is to restrict type arguments to IConvertible which has built in types of “… Boolean, SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Decimal, DateTime, Char, and String.” It is possible other objects will implement IConvertible, though this is rare in practice.

public class Cup<T> where T : IConvertible
{
    // ...
}

default constructor

Only types that contain a default constructor will be allowed. This includes value types and classes that contain a default (parameterless) constructor

public class Cup<T> where T : new
{
    // ...
}

inheritance and implementation

Only types that inherit from a certain base class or implement a certain interface can be supplied.

public class Cup<T> where T : Beverage
{
    // ...
}


public class Cup<T> where T : IBeer
{
    // ...
}

The constraint can even reference another type parameter:

public class Cup<T, U> where U : T
{
    // ...
}

Multiple constraints can be specified for a type argument:

public class Cup<T> where T : class, new()
{
    // ...
}

The previous examples show generic constraints on a class definition, but constraints can be used anywhere a type argument is supplied: classes, structs, interfaces, methods, etc.

where can also be a LINQ clause. In this case it is analogous to WHERE in SQL:

int[] nums = { 5, 2, 1, 3, 9, 8, 6, 7, 2, 0 };

var query =
    from num in nums 
    where num < 5
    select num;

    foreach (var n in query)
    {
        Console.Write(n + " ");
    }
    // prints 2 1 3 2 0

extern

The extern keyword is used to declare methods that are implemented externally. This can be used in conjunction with the DllImport attribute to call into unmanaged code using Interop services. which in this case it will come with static modifier

For Example:

using System.Runtime.InteropServices;
public class MyClass
{
    [DllImport("User32.dll")]
    private static extern int SetForegroundWindow(IntPtr point);

    public void ActivateProcessWindow(Process p)
    {
        SetForegroundWindow(p.MainWindowHandle);
    }
}

This uses the SetForegroundWindow method imported from the User32.dll library

This can also be used to define an external assembly alias. which let us to reference different versions of same components from single assembly.

To reference two assemblies with the same fully-qualified type names, an alias must be specified at a command prompt, as follows:

/r:GridV1=grid.dll
/r:GridV2=grid20.dll

This creates the external aliases GridV1 and GridV2. To use these aliases from within a program, reference them by using the extern keyword. For example:

extern alias GridV1;
extern alias GridV2;

bool

Keyword for storing the Boolean values true and false. bool is an alias of System.Boolean.

The default value of a bool is false.

bool b; // default value is false
b = true; // true
b = ((5 + 2) == 6); // false

For a bool to allow null values it must be initialized as a bool?.

The default value of a bool? is null.

bool? a // default value is null

when

The when is a keyword added in C# 6, and it is used for exception filtering.

Before the introduction of the when keyword, you could have had one catch clause for each type of exception; with the addition of the keyword, a more fine-grained control is now possible.

A when expression is attached to a catch branch, and only if the when condition is true, the catch clause will be executed. It is possible to have several catch clauses with the same exception class types, and different when conditions.

private void CatchException(Action action)
{
    try
    {
        action.Invoke();
    }
    
    // exception filter
    catch (Exception ex) when (ex.Message.Contains("when"))
    {
        Console.WriteLine("Caught an exception with when");
    }

    catch (Exception ex)
    {
        Console.WriteLine("Caught an exception without when");
    }
}

private void Method1() { throw new Exception("message for exception with when"); }
private void Method2() { throw new Exception("message for general exception"); }


CatchException(Method1);
CatchException(Method2);

unchecked

The unchecked keyword prevents the compiler from checking for overflows/underflows.

For example:

const int ConstantMax = int.MaxValue;
unchecked
{
    int1 = 2147483647 + 10;
}
int1 = unchecked(ConstantMax + 10);

Without the unchecked keyword, neither of the two addition operations will compile.

When is this useful?

This is useful as it may help speed up calculations that definitely will not overflow since checking for overflow takes time, or when an overflow/underflow is desired behavior (for instance, when generating a hash code).

void

The reserved word "void" is an alias of System.Void type, and has two uses:

  1. Declare a method that doesn’t have a return value:

    public void DoSomething() { // Do some work, don’t return any value to the caller. }

A method with a return type of void can still have the return keyword in its body. This is useful when you want to exit the method’s execution and return the flow to the caller:

public void DoSomething()
{
    // Do some work...

    if (condition)
        return;

    // Do some more work if the condition evaluated to false.
}
  1. Declare a pointer to an unknown type in an unsafe context.

In an unsafe context, a type may be a pointer type, a value type, or a reference type. A pointer type declaration is usually type* identifier, where the type is a known type - i.e int* myInt, but can also be void* identifier, where the type is unknown.

Note that declaring a void pointer type is discouraged by Microsoft.

if, if…else, if… else if


The if statement is used to control the flow of the program. An if statement identifies which statement to run based on the value of a Boolean expression.

For a single statement, the braces{} are optional but recommended.

int a = 4;
if(a % 2 == 0) 
{
     Console.WriteLine("a contains an even number");
}
// output: "a contains an even number"

The if can also have an else clause, that will be executed in case the condition evaluates to false:

int a = 5;
if(a % 2 == 0) 
{
     Console.WriteLine("a contains an even number");
}
else
{
     Console.WriteLine("a contains an odd number");
}
// output: "a contains an odd number"

The ifelse if construct lets you specify multiple conditions:

int a = 9;
if(a % 2 == 0) 
{
     Console.WriteLine("a contains an even number");
}
else if(a % 3 == 0) 
{
     Console.WriteLine("a contains an odd number that is a multiple of 3"); 
}
else
{
     Console.WriteLine("a contains an odd number");
}
// output: "a contains an odd number that is a multiple of 3"

Important to note that if a condition is met in the above example , the control skips other tests and jumps to the end of that particular if else construct.So, the order of tests is important if you are using if .. else if construct

C# Boolean expressions use short-circuit evaluation. This is important in cases where evaluating conditions may have side effects:

if (someBooleanMethodWithSideEffects() && someOtherBooleanMethodWithSideEffects()) {
  //...
}

There’s no guarantee that someOtherBooleanMethodWithSideEffects will actually run.

It’s also important in cases where earlier conditions ensure that it’s “safe” to evaluate later ones. For example:

if (someCollection != null && someCollection.Count > 0) {
   // ..
}

The order is very important in this case because, if we reverse the order:

if (someCollection.Count > 0 && someCollection != null) {

it will throw a NullReferenceException if someCollection is null.

do

The do operator iterates over a block of code until a conditional query equals false. The do-while loop can also be interrupted by a goto, return, break or throw statement.

The syntax for the do keyword is:

do { code block; } while( condition );

Example:

int i = 0;

do
{
    Console.WriteLine("Do is on loop number {0}.", i);
} while (i++ < 5);

Output:

“Do is on loop number 1.” \

“Do is on loop number 2.”
“Do is on loop number 3.”
“Do is on loop number 4.”
“Do is on loop number 5.”

Unlike the while loop, the do-while loop is Exit Controlled. This means that the do-while loop would execute its statements at least once, even if the condition fails the first time.

bool a = false;

do
{
    Console.WriteLine("This will be printed once, even if a is false.");
} while (a == true);

operator

Most of the built-in operators (including conversion operators) can be overloaded by using the operator keyword along with the public and static modifiers.

The operators comes in three forms: unary operators, binary operators and conversion operators.

Unary and binary operators requires at least one parameter of same type as the containing type, and some requires a complementary matching operator.

Conversion operators must convert to or from the enclosing type.

public struct Vector32
{
    
    public Vector32(int x, int y)
    {
        X = x;
        Y = y;
    }
    
    public int X { get; }
    public int Y { get; }

    public static bool operator ==(Vector32 left, Vector32 right)
        => left.X == right.X && left.Y == right.Y;

    public static bool operator !=(Vector32 left, Vector32 right)
        => !(left == right);

    public static Vector32 operator +(Vector32 left, Vector32 right)
        => new Vector32(left.X + right.X, left.Y + right.Y);

    public static Vector32 operator +(Vector32 left, int right)
        => new Vector32(left.X + right, left.Y + right);

    public static Vector32 operator +(int left, Vector32 right)
        => right + left;

    public static Vector32 operator -(Vector32 left, Vector32 right)
        => new Vector32(left.X - right.X, left.Y - right.Y);

    public static Vector32 operator -(Vector32 left, int right)
        => new Vector32(left.X - right, left.Y - right);

    public static Vector32 operator -(int left, Vector32 right)
        => right - left;

    public static implicit operator Vector64(Vector32 vector)
        => new Vector64(vector.X, vector.Y);

    public override string ToString() => $"{{{X}, {Y}}}";

}

public struct Vector64
{

    public Vector64(long x, long y)
    {
        X = x;
        Y = y;
    }

    public long X { get; }
    public long Y { get; }

    public override string ToString() => $"{{{X}, {Y}}}";

}

Example

var vector1 = new Vector32(15, 39);
var vector2 = new Vector32(87, 64);
        
Console.WriteLine(vector1 == vector2); // false
Console.WriteLine(vector1 != vector2); // true
Console.WriteLine(vector1 + vector2);  // {102, 103}
Console.WriteLine(vector1 - vector2);  // {-72, -25}

struct

A struct type is a value type that is typically used to encapsulate small groups of related variables, such as the coordinates of a rectangle or the characteristics of an item in an inventory.

Classes are reference types, structs are value types.

using static System.Console;

namespace ConsoleApplication1
{
    struct Point
    {
        public int X;
        public int Y;

        public override string ToString()
        {
            return $"X = {X}, Y = {Y}";
        }

        public void Display(string name)
        {
            WriteLine(name + ": " + ToString());
        }
    }

    class Program
    {
        static void Main()
        {
            var point1 = new Point {X = 10, Y = 20};
            // it's not a reference but value type
            var point2 = point1;
            point2.X = 777;
            point2.Y = 888;
            point1.Display(nameof(point1)); // point1: X = 10, Y = 20
            point2.Display(nameof(point2)); // point2: X = 777, Y = 888

            ReadKey();
        }
    }
}

Structs can also contain constructors, constants, fields, methods, properties, indexers, operators, events, and nested types, although if several such members are required, you should consider making your type a class instead.


Some suggestions from MS on when to use struct and when to use class:

CONSIDER

defining a struct instead of a class if instances of the type are small and commonly short-lived or are commonly embedded in other objects.

AVOID

defining a struct unless the type has all of the following characteristics:

  • It logically represents a single value, similar to primitive types (int, double, etc.)
  • It has an instance size under 16 bytes.
  • It is immutable.
  • It will not have to be boxed frequently.

switch

The switch statement is a control statement that selects a switch section to execute from a list of candidates. A switch statement includes one or more switch sections. Each switch section contains one or more case labels followed by one or more statements. If no case label contains a matching value, control is transferred to the default section, if there is one. Case fall-through is not supported in C#, strictly speaking. However, if 1 or more case labels are empty, execution will follow the code of the next case block which contains code. This allows grouping of multiple case labels with the same implementation. In the following example, if month equals 12, the code in case 2 will be executed since the case labels 12 1 and 2 are grouped. If a case block is not empty, a break must be present before the next case label, otherwise the compiler will flag an error.

int month = DateTime.Now.Month; // this is expected to be 1-12 for Jan-Dec

switch (month)
{
    case 12: 
    case 1: 
    case 2:
        Console.WriteLine("Winter");
        break;
    case 3: 
    case 4: 
    case 5:
        Console.WriteLine("Spring");
        break;
    case 6: 
    case 7: 
    case 8:
        Console.WriteLine("Summer");
        break;
    case 9:     
    case 10: 
    case 11:
        Console.WriteLine("Autumn");
        break;
    default:
        Console.WriteLine("Incorrect month index");
        break;
}

A case can only be labeled by a value known at compile time (e.g. 1, "str", Enum.A), so a variable isn’t a valid case label, but a const or an Enum value is (as well as any literal value).

interface

An interface contains the signatures of methods, properties and events. The derived classes defines the members as the interface contains only the declaration of the members.

An interface is declared using the interface keyword.

interface IProduct
{
    decimal Price { get; }
}

class Product : IProduct
{
    const decimal vat = 0.2M;
    
    public Product(decimal price)
    {
        _price = price;
    }
    
    private decimal _price;
    public decimal Price { get { return _price * (1 + vat); } }
}

unsafe

The unsafe keyword can be used in type or method declarations or to declare an inline block.

The purpose of this keyword is to enable the use of the unsafe subset of C# for the block in question. The unsafe subset includes features like pointers, stack allocation, C-like arrays, and so on.

Unsafe code is not verifiable and that’s why its usage is discouraged. Compilation of unsafe code requires passing a switch to the C# compiler. Additionally, the CLR requires that the running assembly has full trust.

Despite these limitations, unsafe code has valid usages in making some operations more performant (e.g. array indexing) or easier (e.g. interop with some unmanaged libraries).

As a very simple example

// compile with /unsafe
class UnsafeTest
{
   unsafe static void SquarePtrParam(int* p)
   {
      *p *= *p; // the '*' dereferences the pointer.
      //Since we passed in "the address of i", this becomes "i *= i"
   }

   unsafe static void Main()
   {
      int i = 5;
      // Unsafe method: uses address-of operator (&):
      SquarePtrParam(&i); // "&i" means "the address of i". The behavior is similar to "ref i"
      Console.WriteLine(i); // Output: 25
   }
}

While working with pointers, we can change the values of memory locations directly, rather than having to address them by name. Note that this often requires the use of the fixed keyword to prevent possible memory corruption as the garbage collector moves things around (otherwise, you may get error CS0212). Since a variable that has been “fixed” cannot be written to, we also often have to have a second pointer that starts out pointing to the same location as the first.

void Main()
{
    int[] intArray = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    UnsafeSquareArray(intArray);
    foreach(int i in intArray)
        Console.WriteLine(i);
}

unsafe static void UnsafeSquareArray(int[] pArr)
{
    int len = pArr.Length;

    //in C or C++, we could say
    // int* a = &(pArr[0])
    // however, C# requires you to "fix" the variable first 
    fixed(int* fixedPointer = &(pArr[0]))
    {
        //Declare a new int pointer because "fixedPointer" cannot be written to.
        // "p" points to the same address space, but we can modify it
        int* p = fixedPointer;

        for (int i = 0; i < len; i++)
        {
            *p *= *p; //square the value, just like we did in SquarePtrParam, above
            p++;      //move the pointer to the next memory space.
                      // NOTE that the pointer will move 4 bytes since "p" is an
                      // int pointer and an int takes 4 bytes

            //the above 2 lines could be written as one, like this:
            // "*p *= *p++;"
        }
    }
}

Output:

1
4
9
16
25
36
49
64
81
100

unsafe also allows the use of stackalloc which will allocate memory on the stack like _alloca in the C run-time library. We can modify the above example to use stackalloc as follows:

unsafe void Main()
{
    const int len=10;
    int* seedArray = stackalloc int[len];
    
    //We can no longer use the initializer "{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}" as before.
    // We have at least 2 options to populate the array. The end result of either
    // option will be the same (doing both will also be the same here).

    //FIRST OPTION:
    int* p = seedArray; // we don't want to lose where the array starts, so we
                        // create a shadow copy of the pointer
    for(int i=1; i<=len; i++)
        *p++ = i;
    //end of first option

    //SECOND OPTION:
    for(int i=0; i<len; i++)
        seedArray[i] = i+1;
    //end of second option

    UnsafeSquareArray(seedArray, len);
    for(int i=0; i< len; i++)
        Console.WriteLine(seedArray[i]);
}

//Now that we are dealing directly in pointers, we don't need to mess around with
// "fixed", which dramatically simplifies the code
unsafe static void UnsafeSquareArray(int* p, int len)
{
    for (int i = 0; i < len; i++)
        *p *= *p++;
}

(Output is the same as above)

implicit

The implicit keyword is used to overload a conversion operator. For example, you may declare a Fraction class that should automatically be converted to a double when needed, and that can be automatically converted from int:

class Fraction(int numerator, int denominator)
{
    public int Numerator { get; } = numerator;
    public int Denominator { get; } = denominator;
    // ...
    public static implicit operator double(Fraction f)
    {
        return f.Numerator / (double) f.Denominator;
    }
    public static implicit operator Fraction(int i)
    {
        return new Fraction(i, 1);
    }
}

true, false

The true and false keywords have two uses:

  1. As literal Boolean values

    var myTrueBool = true; var myFalseBool = false;

  2. As operators that can be overloaded

    public static bool operator true(MyClass x) { return x.value >= 0; }

    public static bool operator false(MyClass x) { return x.value < 0; }

Overloading the false operator was useful prior to C# 2.0, before the introduction of Nullable types.
A type that overloads the true operator, must also overload the false operator.

string

string is an alias to the .NET datatype System.String, which allows text (sequences of characters) to be stored.

Notation:

string a = "Hello";
var b = "world";
var f = new string(new []{ 'h', 'i', '!' }); // hi!

Each character in the string is encoded in UTF-16, which means that each character will require a minimum 2 bytes of storage space.

ushort

A numeric type used to store 16-bit positive integers. ushort is an alias for System.UInt16, and takes up 2 bytes of memory.

Valid range is 0 to 65535.

ushort a = 50; // 50
ushort b = 65536; // Error, cannot be converted
ushort c = unchecked((ushort)65536); // Overflows (wraps around to 0)

sbyte

A numeric type used to store 8-bit signed integers. sbyte is an alias for System.SByte, and takes up 1 byte of memory. For the unsigned equivalent, use byte.

Valid range is -127 to 127 (the leftover is used to store the sign).

sbyte a = 127; // 127
sbyte b = -127; // -127
sbyte c = 200; // Error, cannot be converted
sbyte d = unchecked((sbyte)129); // -127 (overflows)

var

An implicitly-typed local variable that is strongly typed just as if the user had declared the type. Unlike other variable declarations, the compiler determines the type of variable that this represents based on the value that is assigned to it.

var i = 10; // implicitly typed, the compiler must determine what type of variable this is
int i = 10; // explicitly typed, the type of variable is explicitly stated to the compiler

// Note that these both represent the same type of variable (int) with the same value (10).

Unlike other types of variables, variable definitions with this keyword need to be initialized when declared. This is due to the var keyword representing an implicitly-typed variable.

var i;
i = 10;

// This code will not run as it is not initialized upon declaration.

The var keyword can also be used to create new datatypes on the fly. These new datatypes are known as anonymous types. They are quite useful, as they allow a user to define a set of properties without having to explicitly declare any kind of object type first.

Plain anonymous type

var a = new { number = 1, text = "hi" };

LINQ query that returns an anonymous type

public class Dog
{
    public string Name { get; set; }
    public int Age { get; set; }
}

public class DogWithBreed
{
    public Dog Dog { get; set; }
    public string BreedName  { get; set; }
}

public void GetDogsWithBreedNames()
{
    var db = new DogDataContext(ConnectString);
    var result = from d in db.Dogs
             join b in db.Breeds on d.BreedId equals b.BreedId
             select new 
                    {
                        DogName = d.Name,
                        BreedName = b.BreedName
                    };

    DoStuff(result);
}

You can use var keyword in foreach statement

public bool hasItemInList(List<String> list, string stringToSearch)
{
    foreach(var item in list)
    {
        if( ( (string)item ).equals(stringToSearch) )
            return true;
    }

    return false;
}

delegate

Delegates are types that represent a reference to a method. They are used for passing methods as arguments to other methods.

Delegates can hold static methods, instance methods, anonymous methods, or lambda expressions.

class DelegateExample
{
    public void Run()
    {
        //using class method
        InvokeDelegate( WriteToConsole ); 
        
        //using anonymous method
        DelegateInvoker di = delegate ( string input ) 
        { 
            Console.WriteLine( string.Format( "di: {0} ", input ) );
            return true; 
        };
        InvokeDelegate( di ); 
        
        //using lambda expression
        InvokeDelegate( input => false ); 
    }

    public delegate bool DelegateInvoker( string input );

    public void InvokeDelegate(DelegateInvoker func)
    {
        var ret = func( "hello world" );
        Console.WriteLine( string.Format( " > delegate returned {0}", ret ) );
    }

    public bool WriteToConsole( string input )
    {
        Console.WriteLine( string.Format( "WriteToConsole: '{0}'", input ) );
        return true;
    }
}

When assigning a method to a delegate it is important to note that the method must have the same return type as well as parameters. This differs from ‘normal’ method overloading, where only the parameters define the signature of the method.

Events are built on top of delegates.

event

An event allows the developer to implement a notification pattern.

Simple example

public class Server
{
    // defines the event
    public event EventHandler DataChangeEvent;

    void RaiseEvent()
    {
        var ev = DataChangeEvent;
        if(ev != null)
        {
            ev(this, EventArgs.Empty);
        }
    }
}

public class Client
{
    public void Client(Server server)
    {
        // client subscribes to the server's DataChangeEvent
        server.DataChangeEvent += server_DataChanged;
    }

    private void server_DataChanged(object sender, EventArgs args)
    {
        // notified when the server raises the DataChangeEvent
    }
}

MSDN reference

partial

The keyword partial can be used during type definition of class, struct, or interface to allow the type definition to be split into several files. This is useful to incorporate new features in auto generated code.

File1.cs

namespace A
{
    public partial class Test
    {
        public string Var1 {get;set;}
    }
}

File2.cs

namespace A
{
    public partial class Test
    {
        public string Var2 {get;set;}
    }
}

Note: A class can be split into any number of files. However, all declaration must be under same namespace and the same assembly.

Methods can also be declared partial using the partial keyword. In this case one file will contain only the method definition and another file will contain the implementation.

A partial method has its signature defined in one part of a partial type, and its implementation defined in another part of the type. Partial methods enable class designers to provide method hooks, similar to event handlers, that developers may decide to implement or not. If the developer does not supply an implementation, the compiler removes the signature at compile time. The following conditions apply to partial methods:

  • Signatures in both parts of the partial type must match.
  • The method must return void.
  • No access modifiers are allowed. Partial methods are implicitly private.

— MSDN

File1.cs

namespace A
{
    public partial class Test
    {
        public string Var1 {get;set;}
        public partial Method1(string str);
    }
}

File2.cs

namespace A
{
    public partial class Test
    {
        public string Var2 {get;set;}
        public partial Method1(string str)
        {
            Console.WriteLine(str);
        }
    }
}

Note: The type containing the partial method must also be declared partial.


This modified text is an extract of the original Stack Overflow Documentation created by the contributors and released under CC BY-SA 3.0 This website is not affiliated with Stack Overflow