Overview
C# refresher based on Pluralsight and LinkedIn C#/ .NET learning paths.
Resources
- Pluralsight
- C# Development Fundamentals (learning path)
- ✅ C#: The Big Picture by Mike Woodring
- ✅ C#: Getting Started by Paolo Perrotta
- ✅ Controlling Program Flow in C# by Alex Wolf
- 🔁 Introduction to the C# Type System by Gill Cleeren
- ✅ C# Fundamentals by Scott Allen
- ✅ Beginning C# Collections by Simon Robinson
- 🔁 Object-Oriented Programming Fundamentals in C# by Deborah Kurata
- 🔁 LINQ Fundamentals by Scott Allen
- ✅ Dates and Times in .NET by Filip Ekberg
- ✅ Working with Nulls in C# by Jason Roberts
- ✅ Error Handling in C# with Exceptions by Jason Roberts
- ✅ Getting Started with Asynchronous Programming in .NET by Filip Ekberg
- ✅ C# Interfaces by Jeremy Clark
- 🔁 C# Generics by Thomas Claudius Huber
- ✅ C# Extension Methods by Elton Stoneman
- 🔁 C# Events, Delegates and Lambdas by Dan Wahlin
- ✅ Advanced C# Collections by Simon Robinson
- 🔁 C# Tips and Traps by Jason Roberts
- 🔁 C# Concurrent Collections by Simon Robinson
- Other
- ✅ Algorithms and Data Structures – Part 1 by Robert Horvick
- ✅ Algorithms and Data Structures – Part 2 by Robert Horvick
- ✅ .NET 6 First Look by Gill Cleeren
- C# Development Fundamentals (learning path)
- LinkedIn Learning
- Become a Software Developer (learning path)
- ✅ Learning C# by Alexander Zanfir
- Become a C# Developer (learning path)
- 🔁 Learning C# Algorithms by Reynald Adolphe
- Become a Software Developer (learning path)
- Other
- Tools
Study Notes
High-Level
- C# is a simple general-purpose, multi-paradigm programming language developed by Microsoft
- C# is managed (execution engine, compilation), static, strongly typed, lexically scoped, imperative, declarative, generic, object-oriented, with functional and component-oriented features
- C# is resilient to change/ runtime type safety
- .NET is a free, open-source & cross-platform, developer platform
- .NET Framework — Windows Only
- .NET Core/ .NET 5 — Linux, Mac, Windows, ARM
- Common Language Runtime (CLR)
- Runtime for all .NET languages, Garbage Collection etc.
- Base Class Library (BCL) — a core class library
- Framework Class Library (FCL) — a superset of BCL
- E.g. networking, cryptography etc.
- Common Language Runtime (CLR)
- .NET 6 is a unification of different .NET platforms into one, with a single BCL and SDK
- VS 2022 with Hot Reload, C# 10, ASP.NET Core 6, EF Core 6, C++ updates
- Xamarin is an open source mobile (iOS and Android) development platform based on mono
- to be replaced with MAUI in 2022
- .NET Standard allows to develop cross (.NET) platform
- e.g. Framework, Core, and Xamarin apps can use one library
- Software Development Kit (SDK)
- It contains all the components needed to build apps
Command Line Interface (CLI)
dotnet --info
- list out all environment info, available SDKs, runtimes etc.
dotnet new template
- new project CLI syntax, where the template is console, web, mvc etc.
dotnet run --project src\ProjectName
- (1) dotnet restore
- restore NuGet packages
- (2) dotnet build
- compile source code to DLL (binary/ assembly)
- (1) dotnet restore
Language Basics
Fundamentals – Class, Prop, Method, Control Statements
// simple class definition
public class MyClass
{
public string myField = string.Empty;
public MyClass()
{
}
public void MyMethod(int parameter1, string parameter2)
{
Console.WriteLine($"First {parameter1}, second {parameter2}";
}
public int MyAutoImplementedProperty { get; set; }
private int myPropertyVar;
public int MyProperty
{
get { return myPropertyVar; }
set { myPropertyVar = value; }
}
}
// control flow statements
for(int i = 0; i<1000; i++)
{
if((i%2) == 0)
{
Console.WriteLine($"Even number: {i}");
}
}
switch(var_name):
{
case 'VALUE_1':
DoSomething();
break;
case 'VALUE_2':
DoSomethingElse();
break;
case var x when x == "VALUE_3": // pattern matching
DoSomethingSpecial();
break;
default:
DefaultAction();
break
}
// delegate - function pointer
public delegate string WriteMessageLog(string message);
public void WriteLog()
{
WriteLogDelegate log = new WriteLogDelegate(ReturnMessage); // or just = ReturnMessage;
// C# supports multi-cast delegates, so that you can add more by
// log += AnotherHandler;
var result = log("message");
Assert.Equal("message", result);
}
string ReturnMessage(string message)
{
return message;
}
- C# 8 (VS 2019) — .net core 3.1 (lts), C# 9 — .net 5 (prev)
- C# can inherit multiple interfaces, not classes
Nullable<T>
shortcut isT?
- Ternary operator — condition ? true : false; e.g.
x>2 ? "HIGH" : "LOW";
ReadOnly
variables are assigned at runtime- underlying collections can be still modified if a reference is available/ accessible, unlike immutable types
- A
return
orbreak
statement cannot exit thefinally
block - Variables declared as
var
are assigned data type at the compile time Dynamic
type in C# escapes compile-time type checking!@
can be used to escape reserved keywords (avoid)
Access Modifiers
public // code outside of this class will have access to that field
private // default, available to code within the class definition
protected // accessible withing the same namespace
static // not associated with an instance but the type
Reference vs. Value Type
var a = new Book("Grades"); // reference type, as b is an address to the value
var x = 3; // value type, as x holds actual value not a pointer to the value
Object.ReferenceEquals(ins1, ins2); // checks if the same instance
// Method parameters are always passed by value, unless REF is used
public void PassByValue(int x) {}
public void PassByRef(ref int x){}
// OUT, unlike REF, assumes the vbarioable is already initialized
public void PassOut(out int x){}
Base Data Types
// Value Types
// Non numeric: bool - System.Boolean, char - System.Char, DateTime
- System.DateTime
bool isValid = false;
char grade = 'B';
DateTime currentDateTimeUTC = DateTime.UtcNow;
// Tuple, Struct, Enum
(double, int) t1 = (4.5, 3);
public struct Coords
{
public Coords(double x, double y)
{
X = x;
Y = y;
}
public double X { get; }
public double Y { get; }
public override string ToString() => $"({X}, {Y})";
}
enum Season
{
Spring,
Summer,
Autumn,
Winter
}
// Numbers [bits]
// byte [8] - System.Byte, short [16] - System.Int16, int [32] - System.Int32,
// long [64] - System.Int64, float [32] - System.Single, double [64] - System.Double,
// decimal [128] - System.Decimal
int a = 123;
System.Int32 b = 123;
float myNum = 5.75F;
double myDoubleNum = 5.99D;
decimal b = 2.1m;
// Reference Types
// string - System.String, object - System.Object, dynamic - System.Object
string name = "name";
// Strings are immutable reference type
// Max object memory is 2GB or 1 billion charachters
// @ is used to declare a multi-line string
// Implicit typig with 'var'
var valueAssignedAtCompileTime = 101;
// Since C# is strongly typed, stick to explicit typing for clarity
String Helpers, Interpolation vs. Concatenation
// interpolation
var interpolation = $"hello {args[0]}!";
// concatination
var concatenation = "hello " + args[0]" + "!"
// For constructing large strings use string builder instead of a string to improve performance
// Immutable vs. mutable data type
StringBuilder sb = new StringBuilder();
sb.Append("Item One");
// Working with whitespaces and nulls
string s = " ";
bool nullOrEmpty = string.InNullOrEmpty(x);
bool nullOrWhitespace = string.IsNullOrWhitespace(x);
Collections
In general, there are two types of collections used in C# — non-generic collections and generic collections. The System.Collections
namespace contains the non-generic collection types and System.Collections.Generic
namespace includes the generic collection types. In most cases, it is recommended to use the generic collections because they perform better than non-generic collections (in a single-threaded environment) and minimize exceptions by giving compile-time errors instead.
Array – Single[ ], Multidimensional[ , ], and Jagged[ ][ ]
Arrays are: fixed size, zero-indexed, ordered, the same type items.
Performance: O(1) lookups — by index, O(n) for inserts and deletes in the middle/ beginning of the array, O(1) for inserts and deletes at the end.
// Declare a single-dimensional array of 5 integers
int[] array1 = new int[5];
// Declare and set array element values
int[] array2 = new int[] { 1, 3, 5, 7, 9 };
// Alternative syntax
int[] array3 = { 1, 2, 3, 4, 5, 6 };
// Declare a two dimensional array.
int[,] multiDimensionalArray1 = new int[2, 3];
// Declare and set array element values.
int[,] multiDimensionalArray2 = { { 1, 2, 3 }, { 4, 5, 6 } };
// Declare a jagged array.
int[][] jaggedArray = new int[6][];
// Set the values of the first array in the jagged array structure.
jaggedArray[0] = new int[4] { 1, 2, 3, 4 };
List<T>
Represents a strongly typed list of objects that can be accessed by index. Lists always start empty, and items need to be added. Lists are more flexible than arrays. List<T>
is the equivalent of the ArrayList, which implements IList<T>
. List<T>
performs faster and is less error-prone than the ArrayList
.
// Declare a list of ints
List<int> myIntList = new List<int>();
// Enumerating list elements
foreach (var number in myIntList)
Console.WriteLine(number);
// Adding elements -- appending to the end of the list
List<T>.Add(item);
List<T>.AddRange(items)
// Inserting element -- insert items in a specific position
List<T>.Inster(index, item);
// Removing elements
List<T>.Remove(item);
List<T>.FindIndex(Predicate<T>);
List<T>.RemoveAr(index);
// NOTE: inserting and removing list elements may cause performance issues on large datasets
// Refer to 'data structures and algorithm' post for details.
Dictionary<TKey,TValue>
Key-value pairs. Keys must be unique and cannot be null
. Values can be null
or duplicate. Values can be accessed by passing the associated key
in the indexer e.g. myDictionary[key]
.
// Declare a dict
Dictionary<string, string> openWith = new Dictionary<string, string>();
// Adding items
openWith.Add("txt", "notepad.exe");
// Iterating dict elements
foreach(KeyValuePair<string, string> kvp in myDictionary)
Console.WriteLine("Key = {0}, Value = {1}", kvp.Key, kvp.Value);
// Updating dict elements
var cities = new Dictionary<string, string>()
{
{"UK", "London, Manchester, Birmingham"},
{"USA", "Chicago, New York, Washington"},
{"India", "Mumbai, New Delhi, Pune"}
};
if(cities.ContainsKey("France"))
cities["France"] = "Paris";
// Throws KeyNotFoundException if a key does not exist
// Remove item
cities.Remove("UK");
Nullable Types
Always express design intent rather than deal with nulls using either nullable value types or non-nullable reference types.
Nullable Value Types are instances of Nullable<T> struct. It represents a valid range of values for the selected type plus a null.
// Value Type
bool = true; // can hold true or false
// Nullable Value Type
Nullable<bool> longSyntax = true; // can hold true, false and null
bool? shorthandSyntax = true;
// Strings - reference type (immutable), so that nulls are allowed; helper methods
string text = "sample text";
bool isNullOrEmpty = string.IsNullOrEmpty(text);
bool isNullOrWhitespace = string.IsNullOrWhitespace(text);
// null-forgiving operator !
Exceptions
Exceptions represent any error conditions or unexpected behaviour. In C#, exceptions are objects that inherit from System.Exception
and are organized in a hierarchy. We need to handle exceptions, not crash the program, get a chance to fix/ retry, allow for meaningful messages and graceful exit, and error/ audit logging. In general, exceptions provide more readability and less clutter and can be bubbled up the stack.
// Excepations originate from:
// (1): Standard exceptions provided by the .NET (system, e.g., OutOfMemory, StackOverflow)
// (2): Exceptions provides by frameworks/ library author (3rd party, e.g., JsonSerialization)
// (3): Custom application exceptions (app code)
// Base Hierarchy
// Excetpion > SystemException >> OurOfMemoryException
// >> StackOverflowException
// >> ArgumentException
// >> ArgumentNullException
// >> ArgumentOutOfRangeException
// > ArithmeticException >> DivideByZeroException
// >> OverflowException
// > ApplicationException (depricated)
// > CustomException
// CONSTRUCTORS
// Default message and null inner exception
public Exception();
// User-defined message
public Exception(string message);
// User-defined message, wrapped exception
public Exception(string message, Exception innerException);
// THROWING & HANDLING EXCEPTIONS
// When catching, order blocks from the most to the least specific
try
{
var operation = "/";
throw new ArgumentOutOfRangeException(nameof(operation));
}
catch(ArgumentNullException exception)
{
// Handle null
}
catch(CustomException exception)
{
// Handle custom exception
}
catch
{
// Handle the rest (Syste.Exception), access to exception variable defined above
}
finally
{
// Always executed when control leaves try block
// e.g., call Dispose() to free resources
}
// Correct way to rethrow exceptions is to throw; not throw ex;
// to preserve the current call stack
try
{
}
catch (ArgumentNullException ex) when (ex.ParamName == "operation") // exception filters
{
}
catch (Exception ex)
{
throw;
}
Delegates, Anonymous Methods, and Lambdas
A Delegate holds a reference to a method as a singlecast or multicast type.
Anonymous Method is an inline code that can be used whenever a delegate type is expected.
A Lambda Expression is an anonymous method you can use to create delegates or expression tree types.
using system;
namespace DataAccessLayer
{
public delegate vpooid TestDelegate();
class Program
{
Console
}
}
Extension Methods
Asynchronous Programming
Concurrent Collections