Fact-checked by Grok 2 weeks ago

C Sharp syntax

C# syntax encompasses the formal rules and conventions that govern the structure of programs written in C#, a simple, modern, language developed by and first released in July 2000 as part of the .NET Framework. Designed with goals of robustness, durability, and productivity, C# syntax supports strong type checking, automatic garbage collection, and the creation of distributed software components while ensuring portability for developers familiar with C and C++. The language is case-sensitive, uses semicolons (;) to terminate statements, and curly braces ({}) to delimit blocks, making it syntactically similar to C, C++, , and . At its core, C# syntax organizes into namespaces, which group related types such as , structs, interfaces, delegates, and enumerations to promote modularity and avoid naming conflicts. Programs typically begin with using directives to import (e.g., using [System](/page/System);), followed by a namespace declaration, and contain at least one or top-level statements serving as the , often a static void Main or implicit top-level in a single file. Built-in types like int, double, bool, and string form the foundation, with the language enforcing strong typing where variables must be explicitly declared and compatible operations are verified at . Comments are supported via single-line (//) or multi-line (/* */) formats, and keywords such as if, else, for, while, switch, and foreach drive . C# syntax has evolved through standardized versions managed by Ecma International since 2000, incorporating features like pattern matching, LINQ queries, async/await for asynchronous programming, and tuples for lightweight data structures, all while maintaining backward compatibility. These elements enable both imperative and functional programming paradigms, with automatic memory management handled by the .NET runtime to prevent common errors like memory leaks. The definitive reference for syntax remains the C# Language Specification, which details expressions (value-producing constructs like x + y), statements (action-performing like int x = 42;), and the overall grammar for valid programs.

Basics

Identifiers and Keywords

In C#, identifiers are names used to declare and reference elements such as variables, methods, classes, and namespaces. They must follow specific lexical rules to ensure uniqueness and readability within the language's syntax. Valid identifiers begin with an Identifier_Start_Character, which includes the underscore (_) or any letter character from categories (uppercase letter), (lowercase letter), (titlecase letter), (modifier letter), or (letter number). Subsequent characters can be Identifier_Part_Characters, encompassing decimal digits (), connecting (Pc), combining marks ( or Mc), non-spacing marks, or formatting characters (), in addition to the starting characters. C# supports identifiers, requiring to Unicode Normalization Form C for consistency, and allows Unicode escape sequences (e.g., \u0041 for 'A') within identifiers. Identifiers are case-sensitive, and while consecutive underscores are permitted by the rules, coding conventions recommend avoiding them for clarity. Keywords in C# are predefined, reserved identifiers that hold special meanings to the compiler and cannot be used directly as user-defined identifiers. There are 79 reserved keywords, including literals like true, false, and null, as well as structural elements such as abstract, as, base, bool, break, byte, case, catch, char, checked, class, const, continue, decimal, default, delegate, do, double, else, enum, event, explicit, extern, finally, fixed, float, for, foreach, goto, if, implicit, in, int, interface, internal, is, lock, long, namespace, new, object, operator, out, override, params, private, protected, public, readonly, ref, return, sbyte, sealed, short, sizeof, stackalloc, static, string, struct, switch, this, throw, try, typeof, uint, ulong, unchecked, unsafe, ushort, using, virtual, void, volatile, and while. To use a reserved keyword as an identifier, prefix it with the verbatim identifier symbol @, which escapes the keyword without becoming part of the name itself; for example, @class can name a variable while class remains reserved for type declarations. This mechanism allows interoperability with other languages or legacy code where keywords might conflict. In addition to reserved keywords, C# features contextual keywords, which are not fully reserved but acquire special meanings in limited syntactic contexts, allowing them to serve as identifiers elsewhere. There are 31 such keywords: add, alias, ascending, async, await, by, descending, dynamic, equals, from, get, global, group, into, join, let, nameof, notnull, on, orderby, partial, remove, select, set, unmanaged, value, var, when, where, yield, and field. For instance, var is contextual in variable declarations but can name a type outside that scope, and the @ prefix can still be applied if needed in conflicting contexts. The introduction of contextual keywords evolved to minimize breaking changes in new language versions; for example, partial was added as a contextual keyword in C# 2.0 for partial classes, async and await were introduced similarly in C# 5.0 for asynchronous programming, and field was added in C# 13 for accessing backing fields in auto-implemented properties, without reserving them globally. This design preserves backward compatibility while extending the language's expressiveness.

Literals

In C#, literals are fixed values directly embedded in source code that represent constant data of various types, such as numbers, characters, strings, and booleans, without requiring computation or variable assignment. They form the basis for initializing variables and constants, enabling straightforward representation of immutable values in expressions. The syntax for literals has evolved across C# versions to include modern features like digit separators and raw strings, enhancing readability and expressiveness. Boolean literals consist of the keywords true and false, which represent the two possible values of the bool type. These literals are used in conditional expressions and variable initializations, such as bool isActive = true;. Integer literals support (base-10), (base-16, prefixed with 0x or 0X), and (base-2, prefixed with 0b or 0B) notations, introduced in C# 7.0 for binary. Their type is determined by the value and optional suffixes: no suffix defaults to int, uint, long, or ulong based on magnitude; u or U specifies unsigned (uint or ulong); l or L specifies signed long (long or ulong); and ul, UL, lu, or LU specifies ulong. Digit separators using underscores (_) improve readability for large numbers, a feature added in C# 7.0, and can be placed between digits but not at the start or end. Examples include:
csharp
int decimalLit = 42;
uint hexLit = 0x2A;
int binaryLit = 0b101010;
long largeLit = 1_000_000_000L;  // Digit separator for clarity
ulong unsignedLit = 0xFFUL;
Real literals represent floating-point values in decimal form with an optional fractional part and exponent (using e or E for scientific notation), supporting types double (default or suffixed with d or D), float (suffixed with f or F), and decimal (suffixed with m or M). Digit separators are also allowed here. Examples include:
csharp
double defaultDouble = 3.14159;
[float floatLit = 1.23f;](/page/Float)
[decimal decimalLit = 0.456M;](/page/Decimal)
double scientific = 1.23e4;  // Equivalent to 12300
double separated = 1_234.567_89;
Character literals are enclosed in single quotes and represent a single UTF-16 code unit of type char. They support sequences for special characters: simple escapes like \', \", \\, \0, and \e (for , introduced in C# 13); hexadecimal escapes with \x followed by 1-4 hex digits; and escapes with \u or \U followed by exactly 4 or 8 hex digits, respectively. Examples include:
csharp
char simple = 'A';
char escaped = '\n';  // Newline
char escape = '\e';   // ESCAPE (U+001B), C# 13
char unicode = '\u0041';  // 'A' in Unicode
char hex = '\x41';  // 'A' in hex
String literals represent sequences of characters of type string and come in several forms. Regular string literals use double quotes and require escape sequences for special characters, such as \" for quotes or \n for newlines. Verbatim string literals, prefixed with @, treat backslashes and quotes literally without escapes, ideal for paths or regex patterns. Interpolated strings, prefixed with $, embed expressions inside curly braces {} for dynamic formatting, and can combine with verbatim ($@) or raw forms. Raw string literals, introduced in , use at least three consecutive double quotes (""") to delimit content, allowing unescaped quotes, newlines, and indentation handling via delimiter alignment, which simplifies multiline text like or SQL. string literals, also from , append the u8 suffix to produce Utf8String for efficient byte-encoded strings. Examples include:
csharp
string regular = "Hello, \"World\"!";
string verbatim = @"C:\Path\To\File";
string interpolated = $"Value: {42}";
string raw = """
             {
                 "name": "Example"
             }
             """;  // Multiline JSON without escapes
Utf8String utf8Lit = "Hello"u8;
The null literal is the keyword null, representing a reference that does not refer to any object or an undefined value for nullable types. It serves as the default value for reference types and can be assigned to nullable value types like int?. For example: string emptyRef = null;. Collection expressions, introduced in C# 12, provide a concise literal syntax for initializing arrays, lists, spans, and other collections using square brackets [], supporting element lists, spreads (..), and nested collections. They convert implicitly to supported types without needing explicit constructors. Examples include:
csharp
int[] arrayLit = [1, 2, 3];
List<string> listLit = ["apple", "banana"];
int[][] jagged = [[1, 2], [3, 4]];
int[] spreadLit = [1, ..arrayLit, 4];  // Spreads elements from arrayLit

Variables and Constants

In C#, variables are storage locations associated with a specific type that can hold values of that type during program execution. Local variables can be declared with explicit typing, specifying the type name followed by the variable name and optional initialization, as in int count = 0;. Multiple variables of the same type can be declared in a single statement, such as int x = 1, y = 2;. This explicit approach, available since C# 1.0, ensures the verifies type compatibility at . Implicit typing uses the var keyword to allow the to infer the type from the initializer expression, introduced in C# 3.0. For example, var message = "Hello, World!"; infers string for message. The inferred type is determined at compile time and remains strongly typed thereafter, supporting built-in types, anonymous types, and collection initializers like var numbers = new [List](/page/List)<int> { 1, 2, 3 };. Limitations include requiring initialization in the same statement and prohibiting use for class members or untyped expressions like null alone. Local variable type inference extends to target-typed new expressions in C# 9.0, where the type is inferred from the declaration context rather than the new expression itself. For instance, List<int> list = new(); uses new() to create a List<int> without repeating the type. This simplifies code when the target type is already known, such as in variable assignments or method returns, but requires a clear contextual type for inference. Constants in C# provide immutable values that cannot be modified after declaration. Local constants are declared using the const keyword and must be initialized with a compile-time constant expression, supporting only built-in types like numbers, booleans, strings, or null. An example is const double Pi = 3.14159;, available since C# 1.0. Unlike variables, const members are evaluated at compile time and inlined wherever used, but they cannot be declared in methods with unsafe code or involve dynamic values. For runtime-initialized immutable values, particularly at the class or struct level, the readonly keyword is used on fields. These can be assigned during declaration or in constructors, supporting instance or static contexts, as in public static readonly [string](/page/String) Version = "1.0"; where the value might derive from DateTime.Now. Static readonly fields behave like constants for runtime scenarios but allow evaluation at object creation or static initialization time. Variable scoping defines the region of code where a is accessible. Local variables, declared within methods or blocks, are scoped to their enclosing block, such as { int localVar = 10; }, and inaccessible outside it. Their lifetime begins at scope entry and ends at exit, unless captured by anonymous functions or lambdas, which extends lifetime until the delegate is no longer reachable. Local variables start unassigned and must be definitely assigned before use to avoid errors. Field variables, declared at class or struct level, have class-wide scope and lifetime tied to the instance (for non-static) or application domain (for static). They are automatically initialized to default values, such as 0 for integers. Parameter variables, passed to methods, are scoped to the method's execution and lifetime matches the call duration, with value parameters copied and reference/output parameters aliasing the original. Input parameters (in), introduced in C# 7.2, are read-only aliases with similar scoping. Declaration expressions, added in C# 7.0, enable concise declarations in various contexts like if or using statements, including for or objects. For example, var (name, age) = GetPerson(); declares name and age from the returned , inferring types from the expression. This supports patterns like if (var (x, y) = ParsePoint(input)) for conditional declarations, where variables are scoped to the enclosing block.

Code Blocks

In C#, code blocks are delimited by curly braces {} and serve to group one or more statements into a single compound statement, enabling the definition of local scopes. The syntax for a block is { statement_list? }, where statement_list consists of one or more statements, allowing multiple lines of code to be treated as a unit wherever a single statement is expected. Empty blocks, containing no statements, simply transfer control to their end point upon execution. Local scoping rules ensure that variables and constants declared within a are accessible only from the point of declaration to the end of that , promoting encapsulation and preventing unintended access from outer scopes. For instance, a declared inside a cannot be referenced outside it, even in enclosing blocks, which helps manage resource lifetimes and avoid naming conflicts. This scoping applies uniformly to blocks, with labels within a being visible throughout the and any nested blocks. Many language constructs implicitly introduce blocks, such as bodies, where the sequence of statements following the method signature is enclosed in braces to form a . Similarly, blocks appear implicitly in other declarative contexts to group related statements without requiring explicit braces in all cases. Introduced in C# 6.0, expression-bodied members provide a concise shorthand for defining members that consist of a single expression, using the lambda-like syntax => expression in place of a full block. This applies to s, properties, constructors, and other members, omitting the need for braces and a return statement when the body is a simple expression. For example:
csharp
public string Name => $"{firstName} {lastName}".Trim();
This syntax reduces verbosity while maintaining the same semantic behavior as a braced block containing the expression.

Program Structure

Namespaces and Using Directives

In C#, provide a way to organize code by grouping related types, such as classes, interfaces, structures, enumerations, and delegates, into logical hierarchical containers. This organization helps manage the scope of type names, preventing conflicts in large projects where multiple developers or libraries might define types with the same name. The namespace keyword declares such a scope, and all types defined within it belong to that namespace unless further nested. Namespace declarations can take two forms: block-scoped or file-scoped. In the traditional block-scoped , introduced in C# 1.0, the wraps its contents in curly braces, allowing multiple types and nested declarations within a single file or across files. For example:
csharp
[namespace](/page/Namespace) MyApp.Utilities
{
    [public](/page/Public) [class](/page/Class) HelperClass { }
    
    [namespace](/page/Namespace) Data
    {
        [public](/page/Public) [class](/page/Class) DataProcessor { }
    }
}
This creates a nested MyApp.Utilities.Data for DataProcessor. Block-scoped namespaces are explicitly and can be split across multiple files to form the same , such as MyApp.Utilities declared in separate units. File-scoped namespaces, introduced in C# 10.0, offer a more concise for files containing a single primary , using a terminator instead of braces. All top-level types in the file then belong to that without additional wrapping. For instance:
csharp
namespace MyApp.Utilities;

public class HelperClass { }

namespace MyApp.Utilities.Data;  // Invalid: Multiple file-scoped not allowed in one file

public class DataProcessor { }  // Belongs to MyApp.Utilities
File-scoped declarations must appear before any types in the file and cannot be nested directly; they simplify code in projects with one namespace per file but require careful placement of using directives, as those before the declaration apply globally to the file, while those after are scoped to the namespace. Using directives import namespaces or types to avoid repeatedly qualifying names with the full namespace path, such as referencing Console instead of System.Console. The standard using directive, available since C# 1.0, imports an entire namespace:
csharp
using System;
using System.Collections.Generic;

class Program
{
    List<string> list = new();  // List from System.Collections.Generic
}
This facilitates access to types but does not import nested namespaces, requiring separate directives for them. The using static directive, introduced in C# 6.0, imports static members and nested types from a specific type, enabling direct use without the type name prefix. For example:
csharp
using static System.Math;

class Program
{
    double result = PI * Pow(2, 3);  // Static members PI and Pow
}
It applies to any type with static members, regardless of instance members, and can be combined with global scope in later versions. The using alias directive creates a shorthand for a namespace, type, or qualified name, useful for resolving ambiguities:
csharp
using Proj = MyCompany.Project;
using List = System.Collections.Generic.List<int>;  // Alias for a specific type

class Program
{
    Proj.Data data = new();
    List items = new();
}
Aliases, available since C# 1.0 and enhanced in C# 12.0 to support more complex types, help in scenarios with conflicting names from different assemblies. Global using directives, added in C# 10.0, apply project-wide to all files in an assembly, reducing repetition in multi-file projects:
csharp
global using System;
global using static System.Console;
These can be placed in any file but are typically centralized in a shared file like GlobalUsings.cs, and they support static and alias forms. To avoid namespace pollution—where excessive imports lead to name conflicts or cluttered global scope—developers should use targeted using directives rather than importing many overlapping s, qualify names explicitly when ambiguities arise, and leverage aliases or file-scoped structures to maintain clean, hierarchical organization. This practice ensures type resolution remains efficient and predictable, especially in large-scale applications with multiple referenced libraries. Identifiers can always be fully qualified with their to resolve any such issues.

Entry Point and Top-Level Statements

In C# applications, the serves as the starting location for program execution, traditionally defined as a static named Main within a . This must be declared as static and is invoked by the (CLR) upon program startup. The standard signatures include static void Main() for programs without command-line arguments and static void Main([string](/page/String)[] args) to accept arguments as a , where args provides to parameters passed when the program is run. Additionally, Main can return an integer exit code using static [int](/page/INT) Main() or static [int](/page/INT) Main([string](/page/String)[] args), allowing the program to signal success (typically 0) or failure to the operating system. Introduced in C# 7.1, asynchronous entry points extend the Main method to support async operations with signatures such as static async Task Main() or static async Task Main(string[] args), returning a Task to handle asynchronous initialization without blocking the thread. The integer-returning variants, static async Task<int> Main() and static async Task<int> Main(string[] args), enable async code while providing exit codes via await-able tasks. These forms maintain compatibility with the traditional Main while allowing modern asynchronous patterns, such as awaiting I/O operations at startup. C# 9.0 introduced top-level statements, simplifying program structure by permitting executable code directly at the file level without requiring an explicit Main method, enclosing , or declaration for console applications. The automatically generates an appropriate Main based on the code's content—for instance, producing static void Main(string[] args) if no await or return statements are present, or static async Task<int> Main(string[] args) if both asynchronous operations and integer returns are used. This feature is limited to a single file per project to avoid ambiguity, and the generated Main includes the args parameter for command-line arguments, accessible as a non-null string array with args.Length indicating the number of arguments. Return values in top-level statements are handled implicitly; an explicit return expression yields an integer exit code, while unhandled exceptions propagate as process failure. In top-level statement contexts, C# 10 enhances usability through implicit global using directives, automatically including common namespaces like System and System.Threading.Tasks for projects targeting .NET 6 or later, without needing explicit using statements at the file top. These implicit usings apply project-wide, reducing boilerplate in simple programs while preserving the ability to add custom global usings via the global using keyword or project file configuration. For more complex scenarios involving external types, namespaces may be referenced briefly to qualify names within the entry point code.
csharp
// Traditional Main example
public class Program
{
    public static void Main(string[] args)
    {
        if (args.[Length](/page/Length) > 0)
        {
            Console.WriteLine($"Hello, {args[0]}!");
        }
        else
        {
            Console.WriteLine("Hello, World!");
        }
    }
}
csharp
// Top-level statements example (C# 9.0+)
Console.WriteLine("Hello, World!");
if (args.[Length](/page/Length) > 0)
{
    Console.WriteLine($"Argument count: {args.[Length](/page/Length)}");
}
[return 0](/page/Return_0);  // Explicit exit code
csharp
// Async top-level statements example (C# 9.0+ with async)
var delayTask = Task.Delay(1000);
await delayTask;
Console.WriteLine("Async startup complete.");

Data Types

Value Types

Value types in C# represent stored directly within a , typically allocated on the , which enables efficient access and avoids heap allocation overhead. Unlike reference types, value types exhibit copy-by-value semantics, meaning that when assigned or passed as arguments, a complete copy of the is made, ensuring that modifications to the copy do not affect the original instance. This behavior promotes immutability in certain contexts but requires careful consideration for larger structs to prevent performance issues from excessive copying. Value types include both predefined simple types provided by the and user-defined types such as structs and enums.

Predefined Value Types

C# provides a set of built-in value types categorized as numeric types, floating-point types, , , and types, each with specific sizes and value ranges defined by the .NET runtime. These types are aliases for underlying .NET structs, such as int for System.Int32, and are optimized for common operations like and comparisons. The following table summarizes their key characteristics:
TypeSize (bits)Range/Value
sbyte8-128 to 127
byte80 to 255
short16-32,768 to 32,767
ushort160 to 65,535
32-2,147,483,648 to 2,147,483,647
uint320 to 4,294,967,295
long64-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
ulong640 to 18,446,744,073,709,551,615
32±1.5e−45 to ±3.4e38, 7 digits precision
64±5.0e−324 to ±1.7e308, 15-16 digits precision
128±1.0 × 10−28 to ±7.9228 × 10^28, 28-29 digits precision
1 (effective)true or false
16 characters, U+0000 to U+FFFF
These predefined types support implicit conversions within compatible categories, such as from int to long, and are initialized to default values like 0 for numerics and false for bool.

User-Defined Value Types

User-defined value types extend C#'s type system by allowing developers to create custom structs and enums that inherit the stack allocation and copy-by-value behavior of value types. Structs are versatile for encapsulating small, related data and methods, while enums provide named constants for discrete values. Structs are declared using the struct keyword and can include fields, , , and constructors, but they cannot inherit from other types except interfaces. For example:
csharp
public struct Point
{
    public int X;
    public int Y;

    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }

    public void Move(int dx, int dy)
    {
        X += dx;
        Y += dy;
    }
}
Instances of structs are mutable by default, allowing field modifications, but readonly structs restrict changes to prevent unintended copies. Due to their value semantics, passing a struct to a by value creates a full copy, which is efficient for small data but can impact performance for larger ones. Enums are value types defined as a set of named constants backed by an underlying integral type, defaulting to int if unspecified. They are declared with the enum keyword and support explicit values for members. For instance:
csharp
public enum Color
{
    Red = 1,
    Green = 2,
    Blue = 4
}
Since C# 7.3, enums can specify a custom underlying type, such as byte, to optimize storage: public enum E : byte { A = 0, B = 1 }. Enum values are copied by value, and they support bitwise operations when used as flags. Tuples, introduced in C# 7.0, are lightweight value types for grouping a fixed number of elements of arbitrary types, declared using parentheses syntax. They are implemented via System.ValueTuple structs and support both unnamed and named elements for readability:
csharp
[var](/page/Var) pair = (1, "one");  // Unnamed tuple
[var](/page/Var) named = (Number: 1, Name: "one");  // Named tuple
Tuple elements are public fields, making them mutable, and allows easy unpacking, such as var (num, str) = pair;. They provide a concise alternative to custom structs for temporary data grouping. Ref structs, available since C# 7.2, are a specialized category of structs declared with the ref struct keyword, enforced to be stack-allocated only and preventing to reference types like object. This restriction avoids heap escapes, enabling high-performance scenarios like spans:
csharp
public ref struct Coordinate
{
    public int X;
    public int Y;
}
Ref structs cannot be used in fields, arrays, or as type arguments in generics (prior to C# 13), ensuring they remain short-lived and stack-bound, which is crucial for types like Span<T>. Unlike ref structs, other value types can be to reference types when needed, placing a copy on the . In C# 13, ref structs gained additional capabilities while preserving their stack-only nature: they can implement interfaces (though without implicit conversion to interface types to avoid ), be used in async methods and iterators (with restrictions preventing across await or yield boundaries), and serve as type arguments in generic types via the new allows ref struct constraint. The System.Threading.Lock type is also a ref struct, integrated with the lock statement for efficient .

Reference Types

Reference types in C# are types that store to their data rather than the data itself, with instances allocated on the and managed by the garbage collector. This design enables reference semantics, where assigning one reference variable to another results in both pointing to the same object, and modifications through one affect the shared instance. Unlike value types, reference types support as a valid value, indicating no object is referenced, and nullable reference types can be annotated with ? to explicitly allow or disallow assignments at . Arrays are a fundamental reference type in C#, used to store multiple elements of the same type in contiguous memory. Single-dimensional arrays are declared with syntax like type[] identifier;, and initialized using new type[size], for example:
csharp
int[] numbers = new int[5];  // Creates an array of 5 integers, initialized to 0
Multidimensional arrays use comma-separated dimensions, such as int[,] for two-dimensional arrays:
csharp
int[,] matrix = new int[2, 3];  // 2 rows, 3 columns
Jagged arrays, which are arrays of arrays, are declared as type[][] identifier and allow varying lengths for inner arrays:
csharp
int[][] jagged = new int[3][];  // Array of 3 int arrays
jagged[0] = new int[2] { 1, 2 };
jagged[1] = new int[4] { 3, 4, 5, 6 };
Starting in C# 12, collection expressions provide concise initialization using square brackets, applicable to arrays and other collections:
csharp
int[] numbers = [1, 2, 3, 4, 5];  // Single-dimensional
int[,] matrix = [[1, 2, 3], [4, 5, 6]];  // Multidimensional
int[][] jagged = [[1, 2], [3, 4, 5, 6]];  // Jagged
Classes define custom types, declared with the class keyword followed by an identifier and optional access modifiers. A basic declaration is:
csharp
public [class](/page/Class) MyClass { }
Objects are created using the new operator, which allocates memory on the heap and returns a :
csharp
MyClass instance = new();  // Target-typed new (C# 9+), or new MyClass()
This syntax invokes the parameterless constructor if available, producing a that can be . Strings represent sequences of characters and are built-in types aliasing System.String, which is immutable—any modification creates a new string instance. Declaration uses the string keyword, with special literals including ordinary quoted strings ("text"), verbatim strings (@"text" to escape backslashes), and raw string literals (+, """multiline text""" for unescaped content). string literals (+) use u8 suffix, producing ReadOnlySpan<byte>:
csharp
string s1 = "Hello";  // Ordinary
string s2 = @"C:\Path";  // Verbatim
string s3 = """Line1
Line2""";  // Raw
ReadOnlySpan<byte> utf8 = "Hello"u8;
Equality uses value-based operators (==, !=), and indexing provides read-only access. Delegates are reference types that encapsulate methods with a specific , declared using the delegate keyword specifying return type and parameters. A basic declaration is:
csharp
public delegate void MyDelegate(int value);
Instances are created by assigning compatible methods, anonymous methods, or lambda expressions, enabling type-safe function pointers. Other reference types include object, the base type for all types, declared simply as object o = null;. The dynamic type, introduced in C# 4.0, allows runtime binding and bypasses compile-time type checking:
csharp
dynamic d = 5;
d = d + 10;  // Resolved at [runtime](/page/Runtime)
In unsafe contexts, pointers are reference-like types declared with *, such as int* ptr;, requiring the unsafe modifier and option for .

Type Conversions

C# supports conversions between compatible types to enable flexible data manipulation while ensuring . These conversions can be implicit, where the handles the transformation automatically without risk of , or explicit, requiring intervention via operators. Conversions apply to both and types, with special handling for nullable types and scenarios involving the common base type object. The language specification defines built-in conversions for numeric types, reference assignments, and , while allowing user-defined conversions for custom types. Implicit conversions occur without explicit syntax and are guaranteed not to lose information, making them safe for widening operations. For numeric types, these include promotions from smaller integral types to larger ones, such as int to long, or from integral to floating-point types like int to double. For example:
csharp
int value = 42;
long largerValue = value;  // Implicit widening conversion
Reference types support implicit conversions from derived classes to base classes, and value types can implicitly convert to nullable versions of themselves. Nullable value types, declared as T?, allow implicit conversion from T to T?, accommodating null values in value-type contexts. For instance:
csharp
int? nullableInt = 42;  // Implicit from int to int?
These conversions are performed at and have no overhead. Explicit conversions, also known as casts, use the syntax (targetType)expression and are required when data loss or is possible, such as narrowing numeric types from double to int. This can truncate fractional parts or cause in integral conversions. For example:
csharp
double pi = 3.14159;
int truncated = (int)pi;  // Results in 3
In checked contexts, explicit conversions that overflow throw an OverflowException; unchecked contexts silently wrap the value. Contexts can be specified with checked or unchecked keywords, or controlled project-wide via settings, with unchecked as the default for . Example in a checked block:
csharp
int large = 300;
byte small = checked((byte)large);  // Throws OverflowException
This mechanism protects against unintended in critical sections. Boxing and unboxing provide conversions between value types and reference types, bridging the for polymorphism. implicitly converts a value type to object or an it implements, allocating a new object on the . For example:
csharp
int integer = 123;
object boxed = integer;  // [Boxing](/page/Boxing) occurs
Unboxing explicitly extracts the value back, requiring a compatible cast; mismatches throw InvalidCastException. Example:
csharp
int unboxed = (int)boxed;  // [Unboxing](/page/Unboxing)
These operations incur performance costs due to heap allocation () and runtime type checks (), so they should be minimized in performance-sensitive code, such as loops processing value types as objects. The is and as operators facilitate safe type checking and conditional casting, avoiding exceptions from direct casts. The is operator tests compatibility, returning true if the operand can be converted via reference, , , or nullable conversions. For example:
csharp
object obj = "hello";
if (obj is string) { /* safe to cast */ }
The as operator attempts a cast to a reference or nullable type, returning null on failure rather than throwing an exception, useful for reference types. Example:
csharp
object input = "hello";
string result = input as string;  // result is "hello"
These operators promote robust code by enabling pattern-based type verification without overhead from failed casts. User-defined conversions allow classes and structs to define implicit or explicit operators for conversions to or from other types, extending built-in capabilities. These are declared as static methods with implicit or explicit modifiers, enabling seamless integration in expressions while respecting the safety rules of the . Details on implementation are covered in sections.

Operators

Arithmetic and Assignment Operators

C# provides a set of arithmetic operators for performing mathematical operations on numeric types, including integral types such as int, uint, long, and ulong, as well as floating-point types like float, double, and decimal. These operators include both unary forms for single operands and binary forms for two operands, supporting common computations like addition, subtraction, multiplication, division, and modulo. Unary operators also include increment (++) and decrement (--) for modifying values by 1. The unary plus (+) operator returns the value of its operand unchanged, while the unary minus (-) computes the numeric negation of its operand; the latter is not supported for ulong due to the absence of negative values in unsigned types. The increment (++) and decrement (--) operators can be used in prefix or postfix forms: in prefix form (e.g., ++x), the operand is modified before its value is used, whereas in postfix form (e.g., x++), the current value is used before modification. These operators are applicable to all numeric types, with smaller integral types like sbyte or short being promoted to int for the operation. For example:
csharp
int x = 5;
Console.WriteLine(++x);  // Outputs: 6 (x is now 6)
Console.WriteLine(x++);  // Outputs: 6 (x is now 7)
Binary arithmetic operators include addition (+), which sums the operands and also supports string concatenation or delegate combination in non-numeric contexts; subtraction (-), which computes the difference and can remove delegates; multiplication (*), which scales the operands; division (/), which yields the quotient with integer division truncating toward zero and floating-point division producing a precise result; and the modulo (%), which returns the remainder with the sign matching the dividend for integers. Mixed-type operations undergo numeric promotions to ensure compatibility, such as promoting int and double to double. An illustrative example is:
csharp
int a = 10, b = 3;
double result = a / b;  // 3.333... (promoted to double)
Console.WriteLine(a % b);  // Outputs: 1
Console.WriteLine(-10 % 3);  // Outputs: -1 (sign matches [dividend](/page/Dividend))
Overflow in arithmetic operations on integral types can occur when the result exceeds the representable range, leading to in unchecked contexts or exceptions in checked ones. By default, non-constant expressions execute in an unchecked context, where wraps around by truncating high-order bits, but this can be overridden using checked blocks or expressions to enforce runtime checks that throw OverflowException. Constant expressions are checked by default, producing compile-time errors on , though unchecked can suppress this. Floating-point operations handle by yielding positive or negative , and division by zero results in NaN or without regard to context; decimal operations always throw OverflowException or DivideByZeroException. For instance:
csharp
checked {
    int max = int.MaxValue;
    int overflow = max + 1;  // Throws OverflowException
}
int uncheckedOverflow = unchecked(max + 1);  // Wraps to int.MinValue
Assignment operators facilitate storing computed values, with the simple assignment (=) copying the right-hand operand's value to the left-hand operand—for value types, this duplicates the data, while for reference types, it copies the . Compound assignment operators combine arithmetic with assignment, such as += (equivalent to x = x + y), -=, *=, /=, and %=, as well as bitwise &=, |=, ^=, <<=, >>=, and >>>=, evaluating the left operand only once and applying implicit conversions if needed. These are particularly efficient for in-place modifications and support all arithmetic operators. An example demonstrates:
csharp
int sum = 10;
sum += 5;  // sum is now 15
sum *= 2;  // sum is now 30
For computations exceeding the range of built-in integral types, the System.Numerics.BigInteger struct provides arbitrary-precision signed arithmetic, supporting the same operators (+, -, *, /, %) as well as methods like Add and Multiply for explicit calls. It is instantiated via constructors from numeric types, byte arrays, or strings, and handles large values without by dynamically allocating storage. Integer literals in C# are limited to built-in types, but large literals can be converted to BigInteger instances. User-defined types can overload arithmetic operators to customize behavior for custom numeric classes.

Relational and Logical Operators

C# provides relational operators to compare the order or equality of operands, returning a value (true or false) based on the evaluation. These include the equality operator (==), inequality operator (!=), less than (<), greater than (>), less than or equal to (<=), and greater than or equal to (>=). They are applicable to numeric types such as egers (int), floating-point ([double](/page/Double), [float](/page/Float)), characters ([char](/page/Char)), and enums, as well as strings. For numeric types, comparisons evaluate the relative values directly; for example, 5 < 10 yields true, while 7.0 < 5.1 yields false. Type-testing operators include is, which returns true if the operand is compatible with the specified type or pattern, and as, which attempts to cast the operand to the specified type, returning the result if successful or null if not. The is operator, enhanced since C# 7.0 to support pattern matching (e.g., obj is string s), enables type-safe checks without exceptions. The as operator is used for reference or nullable conversions, such as string str = obj as string;, avoiding InvalidCastException. These operators apply to reference types, value types (via boxing if needed), and nullable types. For example:
csharp
object obj = "hello";
if (obj is string text)
{
    Console.WriteLine(text.Length);  // Outputs: 5
}
string result = obj as string ?? "default";
String comparisons using these operators perform ordinal comparisons based on the Unicode code points of characters, treating strings as sequences of characters. For instance, "apple" < "banana" returns true because the first differing character ('a' code 97) is less than ('b' code 98). Special handling applies to floating-point values: comparisons involving NaN (Not a Number), such as double.NaN >= 5.1, always return false to avoid indeterminate results. Enums are compared based on their underlying values. User-defined types can overload these operators, but relational operators like < and > must be overloaded in pairs with <= and >= respectively for consistency. Logical operators in C# manipulate boolean values to perform negation, conjunction, disjunction, and exclusive disjunction. The unary logical negation operator (!) inverts a operand, so !true evaluates to false and !false to true. The binary conditional logical AND (&&) returns true only if both operands are true, with short-circuit evaluation: if the left operand is false, the right is not evaluated, as in false && SecondOperand() where SecondOperand() is skipped. Similarly, the conditional logical OR (||) returns true if either operand is true, short-circuiting if the left is true. Non-conditional logical operators include the binary AND (&), OR (|), and (^), which evaluate both operands regardless of the left operand's value. For types, & yields true if both are true, | if at least one is true, and ^ if exactly one is true; examples include true & false as false and true ^ false as true. These operators also function as bitwise operators on integral types, but when applied to bool, they perform logical operations. The unary bitwise complement (~) is not defined for bool and is used solely for bitwise negation on integers. Bitwise shift operators manipulate the bits of integral types (including char and enums). The left-shift operator (<<) shifts bits left by the specified amount, filling with zeros and equivalent to multiplication by 2^amount for positive values. The signed right-shift (>>) shifts right, preserving the (arithmetic shift for signed types), while the unsigned right-shift (>>>) fills with zeros (). The right operand (shift amount) is converted to and masked to the operand's bit width (e.g., 5 bits for , 6 for long). Shifts by amounts >= width yield zero for unsigned or sign-extended for signed >>. These apply only to integrals, with promotion for smaller types. For example:
csharp
int value = 8;  // Binary: 00001000
Console.WriteLine(value << 1);  // Outputs: 16 (00010000)
Console.WriteLine(-8 >> 1);     // Outputs: -4 (sign preserved)
Console.WriteLine(8 >>> 1);     // Outputs: 4 (11111000 >>>1 = 01111100, but for positive same)
The conditional operator (? :), also known as the ternary operator, provides a concise way to select one of two expressions based on a condition. Its syntax is condition ? consequent : alternative, where the condition must evaluate to true or false; if true, the consequent is evaluated and returned, otherwise the alternative. For example, int temp = [15](/page/15); string message = temp < 20 ? "Cold" : "Warm"; assigns "Cold" to message. The types of consequent and alternative must be compatible, either identical or implicitly convertible to a common type, and only one branch is evaluated, similar to short-circuiting in && and ||. It is right-associative, meaning a ? b : c ? d : e parses as a ? b : (c ? d : e). These operators are foundational in control flow constructs, such as if statements, where relational and logical expressions determine execution paths; for instance, if (x > 0 && y < 10) evaluates the condition before proceeding. As of C# 14, enhancements allow explicit overloading of compound assignments for &, |, and ^ to improve efficiency in user-defined types.
csharp
// Example: Relational operators with numbers and strings
int a = 5, b = 10;
Console.WriteLine(a < b);  // True

string s1 = "hello", s2 = "world";
Console.WriteLine(s1 != s2);  // True

// Logical operators
bool p = true, q = false;
Console.WriteLine(p && q);  // False (short-circuits if p false)
Console.WriteLine(p || q);  // True
Console.WriteLine(!p);      // False

// Conditional operator
int score = 85;
string grade = score >= 90 ? "A" : "B";
Console.WriteLine(grade);   // B

Special Operators

C# provides several special operators that enhance null safety, member and element access, delegate management, and parameter passing, distinct from standard arithmetic or relational operators. These operators, introduced across various language versions, address common programming scenarios such as avoiding reference exceptions, efficient handling, and semantics without requiring explicit pointer manipulation in most cases. The null-coalescing operator ??, available since C# 2.0, evaluates to the left-hand if it is not ; otherwise, it evaluates to the right-hand , enabling concise fallback values for nullable types or reference types. For example, string name = userName ?? "Anonymous"; assigns "Anonymous" if userName is , preventing propagation in expressions. This operator is right-associative, allowing chaining like a ?? b ?? c, and its right-hand side is not evaluated if the left is non-null, supporting . Introduced in C# 8.0, the null-coalescing assignment operator ??= assigns the right-hand to the left-hand only if the left is null, combining null checks with assignment in one step. The syntax left ??= right; ensures the right is evaluated only when necessary, and the left must be assignable such as a , , or indexer, but not a non-nullable value type. An example is List<int>? numbers = null; numbers ??= new List<int>(); numbers.Add(5);, which initializes the list only if it was null. For member access, the dot . , present since C# 1.0, accesses fields, properties, methods, or nested types on an instance or , such as console.WriteLine("Hello");, but it throws a NullReferenceException if the instance is null. The null-conditional ?., added in C# 6.0, mitigates this by returning null if the left operand is null, short-circuiting further access in chains like customer?.Orders?.Count ?? 0. This works with members, methods, indexers, and even delegates (invoking only if non-null), and since , it supports assignment contexts. In unsafe code, the pointer member access -> accesses members through pointers, equivalent to (*pointer).member, requiring an unsafe context and used for low-level operations like fixed (char* pChars = chars) { *pChars = 'a'; } pChars->Length;. The index operator [], fundamental since C# 1.0, accesses array elements or custom indexer properties, with integer indices for arrays throwing IndexOutOfRangeException on bounds violation, as in int[] array = {1, 2}; int value = array[0];. The null-conditional indexer ?[], introduced in C# 6.0, applies null checking before indexing, returning null if the expression is null, useful for safe dictionary or collection access like dictionary?["key"] ?? defaultValue. Since C# 14, assignments like collection?[index] = value; are supported only if the collection is non-null. Delegate operators += and -= manage event subscriptions, where += adds a as an event handler and -= removes it, leveraging delegates for events declared with the event keyword. For instance, button.Click += OnClick; button.Click -= OnClick; attaches and detaches handlers without directly modifying the delegate, ensuring in event raising. These operators work on any delegate type but are idiomatic for events to prevent external modifications. Since C# 7.0, the ref keyword enables reference semantics in parameters, returns, and locals, allowing methods to modify caller variables directly, as in ref int max(ref int a, ref int b) { if (a > b) return ref a; return ref b; }, which returns a reference to the larger value for further manipulation without copying. ref variables alias existing storage, supporting efficient passing of large structures. The out keyword, also enhanced in C# 7.0, requires methods to assign values to parameters before return and allows declaration at the call site without prior initialization, like if (int.TryParse("1", out var number)) { /* use number */ }, facilitating multiple return values idiomatically. The discard _, introduced in C# 7.0, serves as a for unused variables in , out parameters, or , improving code clarity by explicitly ignoring values. In , (string name, _, int age) = person; discards the middle element; for out parameters, DateTime.TryParse(input, out _); suppresses the result; and in switch expressions, case var _: matches any input without binding. Using _ as a regular variable outside these contexts is allowed but may trigger warnings for unused identifiers.

Control Flow

Conditional Statements

Conditional statements in C# enable decision-making by executing different code paths based on Boolean conditions or pattern matches. These include the if statement for simple branching, the switch statement for multi-way selection using labels or patterns, and the switch expression (introduced in C# 8.0) for concise value-based . Conditions typically evaluate expressions, which may incorporate logical operators such as &&, ||, and ! for complex predicates. The if statement evaluates a Boolean expression and executes its associated block if the result is true. Its basic syntax is if (boolean_expression) statement, where statement can be a single expression or a block enclosed in curly braces { }. An optional else clause provides an alternative block for when the condition is false, using the syntax if (boolean_expression) statement else statement. Nested if statements allow for chained conditions, and the ternary conditional operator condition ? expr1 : expr2 offers a compact alternative for simple expressions. For example:
csharp
int temperature = 15;
if (temperature < 20) {
    Console.WriteLine("It's cold.");
} else {
    Console.WriteLine("It's warm.");
}
This executes the first block if temperature < 20 evaluates to true; otherwise, it runs the else block. The switch statement selects one of several code paths by matching an expression against case labels or patterns. In its traditional form (available since early C# versions), it uses constant labels with the syntax switch (expression) { case constant: statements; break; ... default: statements; break; }, where break prevents fall-through to subsequent cases, and default handles unmatched values. Enhanced in C# 7.0, the switch statement supports pattern matching, allowing non-constant patterns like type checks (with when clause from C# 7.0), with the syntax switch (expression) { case pattern when guard: statements; ... }. Later versions, such as C# 9.0 and beyond (including relational, list, and extended property patterns in C# 10.0–13.0), further expand pattern capabilities for ranges, collections, and objects. Execution proceeds to the first matching case and exits via break, return, or throw. An example of C# 7.0 pattern matching:
csharp
object value = 42;
switch (value)
{
    case int i when i > 0:
        Console.WriteLine("Positive integer.");
        break;
    case string s:
        Console.WriteLine("String value.");
        break;
    default:
        Console.WriteLine("Other type.");
        break;
}
Here, type patterns (int, string) and a when guard are used. Multiple patterns can share a single case block, and the default label can appear anywhere. For advanced patterns like relational comparisons (e.g., case < 0), see C# 9.0 documentation. Introduced in C# 8.0, the switch expression provides a more concise, expression-oriented alternative to the switch statement, evaluating to a value rather than controlling flow. Its syntax is expression switch { pattern [when guard] => subexpression, ... }, where arms are separated by commas, and the result is the subexpression of the first matching arm. It requires exhaustiveness—all possible input values must be covered, often via a discard pattern _—to avoid runtime exceptions like SwitchExpressionException in .NET Core 3.0 and later. Guards use when clauses similarly to the statement form. For instance (C# 8.0 compatible):
csharp
string direction = "North";
Orientation result = direction switch
{
    "North" => Orientation.North,
    "South" => Orientation.South,
    "East" => [Orientation](/page/Orientation).Horizontal,
    "West" => [Orientation](/page/Orientation).Horizontal,
    _ => throw new ArgumentException("Invalid direction.")
};
This assigns Orientation.North if direction matches "North"; separate cases handle "East" and "West". The discard _ ensures completeness. Switch expressions support the same patterns as statements, including type, constant, and property patterns (with combinators like or added in C# 9.0), but cannot have empty arms and are evaluated in textual order.

Iteration Statements

Iteration statements in C# provide mechanisms to repeatedly execute a block of based on specified conditions or over collections of , enabling efficient handling of repetitive tasks. These statements include the while, do, for, and foreach constructs, each designed for different iteration scenarios, from condition-based loops to traversing enumerable sequences. They are fundamental to in C# programs, allowing developers to process structures like arrays, lists, and other collections that implement IEnumerable or IEnumerable. The while executes its embedded or as long as a given evaluates to true, checking the condition before each iteration. Its syntax is while (boolean_expression) embedded_statement, where the embedded can be a single or a enclosed in curly braces. For example, the following code prints numbers from 0 to 4:
csharp
[int](/page/INT) n = 0;
while (n < 5)
{
    Console.Write(n);
    n++;
}
This construct is useful for loops where the number of iterations is unknown in advance and depends on a runtime condition. The condition is evaluated at the start of each iteration, so the loop body may execute zero times if the initial condition is false. In contrast, the do statement executes its body at least once before checking the Boolean condition, making it suitable for scenarios where initial execution is required regardless of the condition. Its syntax is do embedded_statement while (boolean_expression);, with the semicolon required after the condition. Using the same example logic:
csharp
int n = 0;
do
{
    Console.Write(n);
    n++;
} while (n < 5);
This ensures the loop runs a minimum of one iteration, differing from while by postponing the condition check until after the body executes. The for statement combines initialization, condition checking, and iteration updating into a single construct, ideal for loops with a known or countable number of iterations. Its syntax is for (for_initializer_opt; for_condition_opt; for_iterator_opt) embedded_statement, where components are optional and separated by semicolons. A typical numeric loop is:
csharp
for (int i = 0; i < 3; i++)
{
    Console.Write(i);
}
The initializer executes once before the loop starts, the condition is checked before each iteration (defaulting to true if omitted), and the iterator runs after each body execution. This structure promotes clear, compact code for index-based iterations. The foreach statement simplifies iteration over collections that implement IEnumerable or IEnumerable, automatically handling enumeration without explicit indexing. Its basic syntax is foreach (type identifier in expression) embedded_statement, where the expression yields an enumerable collection. For instance, iterating over an of integers:
csharp
int[] fibNumbers = { 0, 1, 1, 2, 3, 5, 8, 13 };
foreach (int element in fibNumbers)
{
    Console.Write(element + " ");
}
Starting in C# 7.0, foreach supports , allowing iteration over tuples or objects by unpacking multiple values into variables, such as foreach ((int key, string value) in dictionary) { ... } for entries. Break and continue statements can be used within any statement to exit early or skip to the next . Since C# 8.0, ranges (using the .. operator) enable slicing collections for targeted , as in foreach (var word in words[1..4]) { ... } to process a subset of an or .

Jump and Exception Handling

Jump statements in C# provide mechanisms to alter the normal flow of execution within methods, primarily by terminating or skipping parts of loops and switches or by returning control to the caller. These include the break, continue, , and statements, which enable precise control over program flow without relying on conditional branching alone. Break and continue are particularly useful within iteration statements like for, while, or foreach loops to interrupt or resume iterations, while allows unconditional jumps to labeled statements, though its use is generally discouraged in favor of structured alternatives due to potential for unstructured code. The statement exits the current method, optionally providing a value back to the caller if the method has a non-void return type. The break statement terminates the innermost enclosing loop or switch statement, transferring control to the statement following the terminated construct. For example, in a , it exits early when a condition is met:
csharp
for (int i = 0; i < 10; i++)
{
    if (i == 5) break;  // Exits the [loop](/page/Loop) when i is 5
    Console.WriteLine(i);
}
This outputs numbers 0 through 4. In s, break ensures fall-through does not occur between cases. The continue statement skips the remaining statements in the current iteration of the innermost enclosing loop and proceeds to the next iteration. It cannot be used in switch statements. For instance:
csharp
foreach (int i in Enumerable.Range(0, 10))
{
    if (i % 2 == 0) continue;  // Skips even numbers
    Console.WriteLine(i);  // Outputs odd numbers: 1, 3, 5, 7, 9
}
This advances the loop counter or enumerator without executing the rest of the body. The goto statement transfers control unconditionally to a labeled statement within the same method, using a label defined by an identifier followed by a colon. Labels can appear anywhere a statement is permitted, but goto is often avoided as it can lead to spaghetti code. An example:
csharp
int x = 0;
start: x++;
if (x < 5) goto start;  // Loops until x is 5
Console.WriteLine(x);  // Outputs 5
Jumps into or out of try or catch blocks are restricted to prevent bypassing exception handling. The return statement ends execution of the current method and returns control to the calling code, optionally with a value matching the method's return type. In void methods, it simply exits early. For example:
csharp
int Add(int a, int b)
{
    if (a < 0 || b < 0) return 0;  // Early exit for invalid inputs
    return a + b;
}
If no return statement is reachable in a non-void method, a compiler error occurs. In async methods, return wraps the value in a . Exception handling in C# uses structured statements to detect, manage, and propagate errors, preventing abrupt termination. The try-catch-finally construct wraps potentially error-prone , allowing custom responses to exceptions while ensuring cleanup. Exceptions are objects derived from System.Exception, thrown explicitly or by the . The try statement encloses that might throw an exception, followed by one or more catch blocks to handle specific exception types and an optional finally block for cleanup that executes regardless of exceptions. Catch blocks filter by type, with the most specific first to avoid masking. For example:
csharp
try
{
    int result = 10 / 0;  // Throws DivideByZeroException
}
catch (DivideByZeroException e)
{
    Console.WriteLine($"Error: {e.Message}");  // Handles [division by zero](/page/Division_by_zero)
}
catch (Exception e)
{
    Console.WriteLine($"General error: {e.Message}");  // Catches others
}
finally
{
    Console.WriteLine("Cleanup executed.");  // Always runs
}
The finally runs even if no exception occurs, a executes, or the try completes normally, making it for releasing resources. The throw statement signals an error by creating and propagating an exception instance. It can initialize a new exception or rethrow a caught one. Prior to C# 7.0, rethrowing required throw e;, which loses the original ; from C# 7.0, a simple throw; preserves it. Examples:
csharp
try
{
    // Risky operation
}
catch (InvalidOperationException e)
{
    Console.WriteLine("Handled locally.");
    throw;  // Rethrows, preserving stack trace (C# 7.0+)
}
Or explicitly: throw new ArgumentException("Invalid argument");. Throw can also appear as an expression in conditional contexts since C# 7.0. The using statement simplifies for types implementing IDisposable, automatically calling Dispose() at the end of the block to release unmanaged resources like file handles or database connections, even if exceptions occur. Introduced in C# 1.0 as a block form, C# 8.0 added using declarations for implicit scoping at the variable's lifetime end. Traditional block example:
csharp
using (var file = new FileStream("file.txt", FileMode.Open))
{
    // Use file
}  // Dispose called here
With C# 8.0 declaration:
csharp
using var file = new FileStream("file.txt", FileMode.Open);
// Use file; Dispose called at end of enclosing scope
Multiple resources can be declared in one using, separated by commas. This expands to try-finally under the hood.

Classes and Structs

In C#, classes are reference types declared using the class keyword, optionally preceded by an access modifier such as public or private, followed by the class name and a body enclosed in curly braces. The default access level for a class is internal, meaning it is accessible only within the same , but explicit modifiers like public allow access from any , while private restricts visibility to the containing type. For example:
csharp
public class Person
{
    public string Name { get; set; }
}
This declaration defines a basic that can be instantiated and used across assemblies. Structs, in contrast, are types declared with the struct keyword and similarly support access modifiers. The primary distinction between classes and structs lies in their storage and behavior: classes are allocated on the and accessed via references, enabling shared instances and nullability, whereas structs are typically allocated on the and copied by , which avoids heap allocation overhead but can lead to issues with large or complex due to semantics. Structs are recommended for small, immutable structures like coordinates or dates where equality and are critical, such as in high-frequency operations, while classes suit scenarios requiring polymorphism, , or object identity. For instance:
csharp
public struct Point
{
    public int X { get; set; }
    public int Y { get; set; }
}
Classes support from other classes, allowing derived types to extend base functionality. Introduced in C# 12, primary constructors provide a concise way to declare es or structs by including parameters directly in the type declaration, making them available as fields throughout the type body without needing a separate constructor definition. These parameters capture values at and can be used for initialization or as implicit backing fields, reducing boilerplate while maintaining mutability unless specified otherwise. An example for a class is:
csharp
public class Container(int capacity)
{
    private int _current = 0;
    public void Add() => _current++;  // capacity is accessible here
}
This feature extends to structs as well, promoting cleaner syntax for simple data holders. , added in C# 9.0, offer a specialized syntax for immutable types (record classes) or value types (record structs) that emphasize data and immutability, declared as record followed by the type name and optional primary constructor parameters. A positional record like record Person([string](/page/String) Name, [int](/page/INT) Age); automatically generates properties, constructors, and overrides for comparison based on values, making it ideal for data transfer objects or when structural is needed over . can be mutable if explicitly defined but default to immutability, and record structs provide value-type benefits with built-in support. Also from C# 9.0, init-only properties enable immutability after object initialization by using the init accessor, which allows setting values only during construction via object initializers or constructors, declared as { get; init; }. This prevents post-construction changes, enhancing and in classes or structs, as in:
csharp
public class ImmutablePoint
{
    public int X { get; init; }
    public int Y { get; init; }
}

// Usage: var p = new ImmutablePoint { X = 1, Y = 2 };
Init-only properties integrate seamlessly with and primary constructors to enforce immutability patterns without full record syntax.

Interfaces and

In C#, define contracts that specify the signatures of methods, properties, events, and indexers without providing implementations, enabling multiple types to adhere to a common set of behaviors. An interface declaration begins with the interface keyword, followed by the interface name, and may include members such as method signatures; for example, public interface ILogger { void Log(string message); }. Starting with C# 8.0, support default implementations for members, allowing a body to be provided for methods or properties, which implementers can choose to override if needed. This feature facilitates evolutionary design by permitting additions to existing without breaking existing implementers. To implement an , a or struct uses a colon (:) after its name to list the , followed by providing public for all members; for instance, public class ConsoleLogger : ILogger { public void Log(string message) { Console.WriteLine(message); } }. must match the interface member signatures exactly, and if multiple interfaces declare the same member, the implementing type resolves any conflicts through explicit interface implementation, prefixing the member with the interface name, such as void ILogger.Log(string message) { ... }. C# supports of interfaces, allowing a type to implement several interfaces simultaneously, which promotes and polymorphism across unrelated types. Class inheritance in C# allows a derived to extend a , inheriting its members and enabling polymorphism through the base keyword to access constructors or members, as in public class Derived : Base { public Derived() : base() { } }. To support overriding, members are marked virtual, and derived classes use override to provide new implementations; for example, public virtual void Method() { } in the and public override void Method() { } in the derived. Overrides can be marked sealed to prevent further overriding in subclasses, enhancing control over hierarchies, such as public sealed override void Method() { }. Abstract classes serve as base classes that cannot be instantiated directly and may include abstract members requiring implementation in derived classes, declared with the abstract keyword; for example, public abstract class Shape { public abstract double Area(); }. A derived class must provide concrete implementations for all abstract members, like public class Circle : Shape { public override double Area() { return Math.PI * radius * radius; } }, combining inherited non-abstract members with required overrides. Unlike interfaces, abstract classes can include fields, constructors, and non-abstract methods, providing a partial while enforcing contracts through .

Members and Access

In C#, members of classes and structs include fields for data storage, properties for controlled access to data, methods for operations, constructors for initialization, indexers for array-like access, events for notifications, and destructors for cleanup. These members can be declared with access modifiers that determine their visibility and usage scope within assemblies and inheritance hierarchies. Access control promotes encapsulation by restricting direct access to internal state while allowing controlled interaction. Access modifiers in C# include public for unrestricted access from any code, private for access only within the same or struct, protected for access within the class and derived classes, internal for access within the same , protected internal for access within the same or derived classes in other assemblies, and private protected (introduced in C# 7.2) for access within the same or derived classes in the same . These modifiers apply to all member types and can differ between accessors in properties and indexers. For instance, a property's get accessor might be public while its set is private. Fields typically use private to enforce encapsulation. Fields store data directly and are declared with a type and name, optionally initialized at declaration. They can be marked readonly to allow assignment only during declaration or in a constructor, preventing subsequent modification and enabling compile-time constants if initialized with literals. A readonly field ensures immutability after initialization, useful for configuration values or caches. For example:
csharp
public class Example
{
    private int _x;  // Mutable field
    private readonly int _y = 42;  // Readonly field initialized at declaration
}
In the constructor, _x can be assigned, but _y cannot after declaration. Readonly fields are evaluated at if initialized in constructors, unlike const fields which are compile-time constants. Properties provide a flexible mechanism to read, write, or compute values, as smart with validation or computation logic in accessors. Auto-implemented properties, introduced in C# 3.0, simplify declaration by automatically generating a backing . Expression-bodied properties, available since C# 6.0, use => for concise read-only definitions. Accessors can include get for reading and set for writing, with optional different access modifiers. Starting with C# 13, properties and indexers can be declared as partial, allowing the declaration in one part of a partial class and the implementation (get/set accessors) in another, which is useful for generated code scenarios; for example, one file declares partial int MyProperty { get; } and another implements the accessors. For example:
csharp
public class Example
{
    private int _backingField;
    public int AutoProperty { get; set; }  // Auto-implemented
    public int ExpressionProperty => _backingField * 2;  // Expression-bodied since C# 6.0
    public int ControlledProperty
    {
        get => _backingField;
        private set => _backingField = value > 0 ? value : 0;  // Validation in setter
    }
}
Properties support required modifiers (C# 11.0) to enforce initialization via object initializers or constructors. Methods define behavior and are declared with a return type, name, and parameters, optionally including modifiers like virtual or static. The params modifier allows a variable number of arguments as an , placed last in the parameter . Optional parameters provide default values, enabling callers to omit them. Local functions, introduced in C# 7.0, are private methods nested within another member for encapsulation of helper logic, supporting their own parameters and locals. For example:
csharp
public class Example
{
    public int Sum(int a, int b = 0, params int[] extras)
    {
        int LocalHelper(int x, int y) => x + y;  // Local function since C# 7.0
        return LocalHelper(a, b) + extras.[Sum](/page/Sum)();
    }
}
Invocation uses Sum(1) for defaults or Sum(1, 2, 3, 4) for params. Local functions capture enclosing variables but can be marked static to avoid captures. Constructors initialize instances and are special methods with the class name as signature, optionally parameterized. Instance constructors can chain via this() or base() for base class invocation. Static constructors, parameterless, run once before first access to static members. Primary constructors, introduced in , declare parameters in the class header for direct use as fields or in other constructors. Object initializers, since , allow setting properties post-constructor invocation. For example:
csharp
public [class](/page/Class) Example([int](/page/INT) initialValue)  // Primary constructor since C# 12.0
{
    public [int](/page/INT) [Value](/page/Value) { get; set; } = initialValue;
    
    public Example() : this(0) { }  // Chains to primary
    
    static Example() => Console.WriteLine("Static init");  // Static constructor
}

// Usage
var obj = new Example { [Value](/page/Value) = 42 };  // Object initializer since C# 3.0
Static constructors initialize static fields automatically if not provided. Indexers enable array-like access to class instances using this with parameters, similar to properties but with indexing syntax. They include get and set accessors and support multiple parameters for multidimensional access. For example:
csharp
public [class](/page/Class) IndexerExample
{
    private string[] _values = new string[10];
    public string this[int index]
    {
        get => _values[index];
        set => _values[index] = value;
    }
}

// Usage
var ex = new IndexerExample();
ex[0] = "Hello";
Indexers can be declared in interfaces and overloaded by parameter types. are members that allow a to notify subscribers of occurrences, declared with the event keyword and a delegate type, typically following the EventHandler pattern. They support add and remove accessors for subscription management, with field-like syntax for simplicity. Events prevent direct invocation outside the declaring class, ensuring controlled raising. Detailed delegate usage is covered elsewhere. For example:
csharp
public event EventHandler ValueChanged;
Raising uses ValueChanged?.Invoke(this, EventArgs.Empty);. , known as finalizers, perform cleanup before collection and are declared with ~ClassName(), parameterless and without access modifiers. They cannot be inherited or overloaded and run nondeterministically. Use is discouraged in favor of IDisposable for . For example:
csharp
public class Example
{
    ~Example() => Console.WriteLine("Cleanup");
}
The compiler translates to protected override void Finalize().

Generics and Collections

Generic Types and Methods

Generics in C# provide a mechanism for , allowing classes, interfaces, methods, and delegates to operate on data types specified as parameters, thereby promoting , , and performance without the overhead of casting or boxing. Introduced in C# 2.0 as part of the .NET Framework 2.0, generics enable developers to define type placeholders, known as type parameters, which are replaced with actual types at . This feature is particularly prevalent in collection classes within the .Collections.Generic namespace, such as List, where T represents the element type. Generic classes are declared by appending one or more type parameters in angle brackets after the class name. The basic syntax is class ClassName<T> { }, where T is the type parameter, which can appear in fields, properties, methods, or return types within the class. For example, a simple generic stack class might be defined as follows:
csharp
public class Stack<T>
{
    private T[] elements;
    private int top;

    public Stack(int size)
    {
        elements = new T[size];
        top = -1;
    }

    public void Push(T item)
    {
        if (top + 1 < elements.Length)
            elements[++top] = item;
    }

    public T Pop()
    {
        if (top >= 0)
            return elements[top--];
        throw new InvalidOperationException("Stack is empty");
    }
}
Instances are created by specifying the type argument, such as Stack<int> intStack = new Stack<int>(10);, resulting in a closed constructed type where T is replaced by int during compilation. Generic classes can inherit from other generic or non-generic classes and implement generic interfaces, enhancing flexibility in object-oriented designs. Generic methods extend this polymorphism to individual methods, declared within or outside generic classes, using type parameters independent of the enclosing type. The syntax involves placing the type parameter list before the method's return type, as in T MethodName<T>(T arg) { }. Type inference allows the compiler to deduce the type from arguments, eliminating the need to explicitly specify it in calls. Consider this example of a swapping method:
csharp
public static void Swap<T>(ref T a, ref T b)
{
    T temp = a;
    a = b;
    b = temp;
}
Usage might be int x = 1, y = 2; Swap(ref x, ref y);, where the infers T as . methods support overloading based on type parameters and can be static or instance methods, with the type parameter scope limited to the method body. Type parameters can be constrained using the where clause to restrict the allowable types, ensuring access to specific members or behaviors and improving compile-time error detection. Constraints include interfaces (e.g., where T : IComparable<T>), base classes (e.g., where T : SomeBaseClass), reference types (where T : class), value types (where T : struct), unmanaged types (where T : unmanaged), types with parameterless constructors (where T : new()), and the ability to reference the type parameter itself (where T : U for another parameter U). Multiple constraints are chained, and the new() constraint must appear last. For instance:
csharp
public class Container<T> where T : class, IComparable<T>, new()
{
    public T Value { get; set; } = new T();

    public int Compare(T other)
    {
        return Value.CompareTo(other);
    }
}
This restricts T to reference types implementing with a default constructor. In C# 13, an additional allows ref struct constraint permits ref struct types like in generic algorithms, broadening applicability to stack-allocated structures. The default value for a type parameter T is obtained via default(T), which yields for types and zero-initialized values for value types, facilitating initialization without assumptions about the type. This is commonly used in fields or local variables, as in T item = default(T);. interfaces define contracts with type parameters, similar to classes, using syntax like interface IRepository<T> { T GetById(int id); }. Implementing classes specify the type argument, such as public class UserRepository : IRepository<User> { }, enabling type-safe abstractions for collections or services. interfaces can inherit from other interfaces and support constraints, as seen in types like IEnumerable. Generic delegates mirror this pattern, declared with type parameters for strongly typed callbacks or events, such as public delegate void [Action](/page/Action)<T>(T arg);. Invocation requires specifying the type, e.g., Action<string> print = s => Console.WriteLine(s); print("Hello");, avoiding boxing and enhancing performance in scenarios.

Covariance and Contravariance

In C#, and contravariance are type-safe mechanisms that enable implicit conversions between types, allowing for more flexible assignments while preserving compatibility with the type . permits the use of a more derived type in place of a less derived one, typically for output positions like return types, whereas contravariance allows a less derived type to be used where a more derived one is expected, often for input positions such as method parameters. These features were introduced in C# 4.0 to support variance annotations on type parameters in interfaces and delegates, enhancing code reusability without runtime type errors. Covariance is declared using the out keyword for a generic type , such as in the IEnumerable<out T>, which ensures that the type parameter appears only in output positions (e.g., as return types) and never as input parameters. This allows assignments like IEnumerable<object> enumerable = new List<string>();, where a collection of strings (more derived) can be treated as a collection of objects (less derived), since operations on IEnumerable<T> only read from the collection. The out modifier enforces that no methods in the accept T as an argument, preventing type-unsafe insertions. Contravariance uses the in keyword, as in IComparer<in T>, where the type parameter is used solely in input positions (e.g., method parameters) and not as return types. For instance, if Employee derives from Person, an IComparer<Employee> can be assigned to IComparer<Person>, enabling a comparer designed for employees to compare persons safely, since it will only receive more specific types. This is possible because contravariant positions ensure that inputs are treated as the more general base type. Variance annotations apply exclusively to reference types and are not supported for value types, as substituting value types could lead to or issues that violate . In delegates, allows a returning a more derived type to match a delegate expecting a less derived type (e.g., Func<object> func = () => "string";), while contravariance permits a accepting a less derived to match a delegate expecting a more derived one (e.g., Action<string> action = obj => Console.WriteLine(obj); where the method takes object). Arrays support for reference types, such as assigning string[] to object[], but this is not type-safe at compile time and may cause runtime exceptions if elements are modified incompatibly.

Enumerators and Iterators

In C#, enumeration over collections is facilitated by the IEnumerable<T> and IEnumerator<T> interfaces, which provide a standardized way to iterate through sequences of elements without exposing the underlying . The IEnumerable<T> interface, defined in the System.Collections.Generic namespace, declares a single method, GetEnumerator(), which returns an IEnumerator<T> object for to the collection's elements. This interface inherits from the non-generic IEnumerable and supports on its type parameter T, allowing it to be used with derived types in contexts. The IEnumerator<T> interface extends IEnumerator and IDisposable, enabling the actual traversal of the collection. It includes a Current property to access the element at the current position and methods such as MoveNext(), which advances the enumerator to the next element and returns true if one exists (otherwise false), and Reset(), which sets the enumerator back to its initial state before the first element—though implementations may throw a NotSupportedException if resetting is not supported. The enumerator begins positioned before the first element, requiring an initial call to MoveNext() to access the first item, and it enforces read-only access to prevent modifications during iteration. Modifying the collection while enumerating can lead to undefined behavior, and enumerators are not thread-safe by default. Iterator methods, introduced in C# 2.0, simplify the implementation of IEnumerable<T> by allowing developers to define sequences using the yield contextual keyword within , properties, or operators that return IEnumerable<T>. An iterator method appears as a standard method but uses yield return to produce each element on demand, pausing execution after each yield until the next element is requested. For example:
csharp
public IEnumerable<int> GetEvenNumbers(int start, int count)
{
    for (int i = start; i < start + count; i += 2)
    {
        yield return i;
    }
}
This method generates even numbers lazily without storing the entire sequence in memory. The yield break statement can be used explicitly to end iteration early, though it is optional as reaching the end of the method body implicitly terminates the sequence. The foreach loop in C# relies on these interfaces to consume iterators, translating into calls to GetEnumerator(), MoveNext(), and Current. Under the hood, the C# compiler transforms iterator methods containing yield return into a state machine to manage the deferred execution and resumption of the sequence. This involves generating a private nested class that implements IEnumerator<T>, complete with fields to track the current state (often via an integer enum or switch-based logic), local variables from the iterator, and the yielded value. The MoveNext() method in this generated class uses a switch statement to resume execution from the point of the last yield return, advancing the state accordingly—for instance, transitioning from an initial state to yield the first element, then to subsequent states for each additional yield, until completion or yield break. This compiler-generated code ensures efficient, resumable iteration without manual state management by the developer. Iterators enable deferral of computation in foreach loops, meaning the sequence is not fully evaluated until elements are actually consumed, promoting for memory efficiency—particularly beneficial for large or sequences. For example, in the GetEvenNumbers method above, execution pauses after each yield return, generating values only as the foreach loop requests them via MoveNext(). This deferral avoids upfront allocation of the entire result set, contrasting with traditional methods that return a complete collection.

Functional and Anonymous Features

Delegates, Lambdas, and Anonymous Methods

In C#, delegates are type-safe function pointers that reference methods with a specific signature, enabling methods to be passed as parameters and facilitating callback mechanisms. They are object-oriented wrappers around methods, supporting both static and instance methods, and can be multicast to invoke multiple methods sequentially. Delegates are declared using the delegate keyword, specifying the return type and parameters, such as public delegate int PerformCalculation(int x, int y);. To instantiate a delegate, assign a compatible to it, as in PerformCalculation calc = Add;, where Add is a matching the public static [int](/page/INT) Add([int](/page/INT) a, [int](/page/INT) b) { return a + b; }. occurs by calling the delegate instance like a , e.g., int result = calc(5, 3);, which executes the referenced and returns the value. For multicast delegates, combine instances using the + or += operator, such as calc += Subtract;, and invoke to call all chained s in order. Anonymous methods, introduced in C# 2.0, provide a way to define inline code blocks without naming the method, using the delegate keyword. The syntax is delegate (parameters) { body }, optionally omitting parameters if none are needed. For example, Action<string> printMessage = delegate(string msg) { Console.WriteLine(msg); }; printMessage("Hello"); creates and invokes an anonymous method that prints the message. This form allows capturing outer variables but is largely superseded by lambda expressions for brevity. Lambda expressions, added in C# 3.0, offer a more concise syntax for functions using the => to separate parameters from the body. They come in expression form (x) => x * 2 for single expressions or statement form (x, y) => { return x + y; } for blocks. An example is Func<int, int> square = x => x * x; int result = square(4);, which computes and returns 16. Async lambdas, introduced in C# 5.0 with the async and await keywords, enable asynchronous methods, such as Func<Task> asyncOp = async () => { await Task.Delay(1000); };. Both anonymous methods and lambdas support capturing variables from the enclosing scope, forming closures that extend the lifetime of those variables until the delegate is garbage-collected. For instance, int counter = 0; Action increment = () => counter++; increment(); Console.WriteLine(counter); outputs 1, as the lambda modifies the captured counter. Captured variables must be definitely assigned beforehand and cannot include ref, out, or in parameters directly; use the static modifier on the lambda to prevent capture if needed. C# provides predefined generic delegates to simplify common scenarios: Action<T> for void-returning methods with up to 16 parameters, and Func<T, TResult> (and variants) for methods returning a value. For example, Action<string> logger = msg => Console.WriteLine(msg); logger("Log entry"); uses Action for logging without a return. Similarly, Func<int, bool> isEven = n => n % 2 == 0; bool even = isEven(4); employs Func for a predicate returning true. These are often used in event handling, such as subscribing to events with eventHandler += obj => Process(obj);.

LINQ and Query Expressions

LINQ, or , is a set of features in C# that enables querying data from various sources using a declarative syntax integrated directly into the language. Query expressions provide a SQL-like syntax for operations such as filtering, sorting, grouping, and joining, allowing developers to write type-safe queries that are checked at . This syntax is built atop extension methods from the System.Linq namespace, facilitating a consistent model for data manipulation across in-memory collections, databases, and XML. The core of query expressions begins with the from clause, which specifies the data source and introduces a range to iterate over its elements. For instance, from num in numbers declares numbers as the source and num as the representing each element. Subsequent clauses refine the query: where applies a condition to filter elements, such as where num > 5; orderby sorts the results ascending or descending, e.g., orderby num descending; let introduces a variable to store an intermediate expression, like let doubled = num * 2; join correlates two sources based on key equality, as in join prod in products on cat.Id equals prod.CategoryId; group partitions elements by a key for aggregation, e.g., group num by num % 2; and select projects the final output shape, such as select new { Value = num, Doubled = doubled }. These clauses can be combined in a fluent, readable manner, resembling declarative languages like SQL. Under the hood, query expressions are syntactic sugar that the C# compiler translates into method calls on standard query operators from System.Linq.Enumerable. For example, the query from num in numbers where num > 5 select num compiles to numbers.Where(num => num > 5).Select(num => num). Similarly, join maps to Join, group to GroupBy, and orderby to OrderBy or OrderByDescending. This translation preserves the semantics while allowing the use of lambda expressions for conditions and projections. LINQ supports multiple providers for different data sources, with LINQ to Objects targeting in-memory collections that implement IEnumerable<T>, enabling queries on arrays, lists, and other enumerable types without external dependencies. to XML, via the System.Xml.Linq namespace, allows querying and manipulating XML documents as object hierarchies, such as using XDocument and XElement with query expressions to extract nodes. Other providers include LINQ to SQL (via ) for database queries and custom IQueryable<T> implementations for web services or specialized stores. A key aspect of queries is deferred execution, where the query is not evaluated until its results are enumerated, such as in a foreach loop or via ToList(). This laziness improves performance by avoiding unnecessary computations. For IQueryable<T> sources, the compiler builds expression trees—data structures representing the query as code—rather than delegates, allowing providers to translate them into source-specific operations like SQL. In contrast, to Objects uses delegate-based execution trees.
csharp
// Example: LINQ to Objects query
int[] numbers = { 0, 1, 2, 3, 4, 5, 6 };
var evenSquares = from num in numbers
                  where num % 2 == 0
                  let square = num * num
                  orderby square
                  select square;
// Equivalent method syntax: numbers.Where(num => num % 2 == 0).Select(num => num * num).OrderBy(x => x)
foreach (int sq in evenSquares)
{
    Console.WriteLine(sq);  // Outputs on enumeration: 0, 4, 16, 36
}
This example demonstrates filtering even numbers, computing squares, sorting, and projecting, with execution deferred until the foreach.

Extension Methods and Local Functions

Extension methods, introduced in C# 3.0, allow developers to add functionality to existing types without modifying their source code or creating derived types. These are implemented as static methods within a non-nested, non-generic static class, where the first parameter is modified with the this keyword to indicate the type being extended. For instance, to extend the string type with a WordCount method, the declaration would appear as follows:
csharp
public static class StringExtensions
{
    public static int WordCount(this string str)
    {
        return str.Split(new char[] { ' ', '.', '?' }, StringSplitOptions.RemoveEmptyEntries).Length;
    }
}
This enables invocation as if it were an instance method: "Hello world".WordCount(); returns 2. Extension methods must be imported via a using directive to be accessible, and they have lower precedence than instance methods of the same name on the extended type. They cannot access private or protected members of the extended type and are particularly useful for enhancing framework types, such as those in where they extend IEnumerable<T> for query operations. Guidelines recommend avoiding frivolous extensions on core types like object and using descriptive namespaces to prevent conflicts. Local functions, added in C# 7.0, provide a way to declare private, nested s within another member, such as a or accessor, restricting their to the containing member only. Unlike lambdas, local functions are named and do not require delegate creation unless explicitly converted, potentially avoiding allocations. The syntax resembles a standard declaration but without access modifiers:
csharp
public int CalculateSum(int start, int end)
{
    int LocalSum(int i, int j) => i + j;
    return Enumerable.Range(start, end).Sum(i => LocalSum(i, end - i));
}
Local functions can capture variables from the enclosing scope, improving encapsulation for helper logic. In C# 8.0, the static modifier was introduced for local functions, preventing capture of local variables or instance state to enhance performance and clarity. A static local example:
csharp
public void ProcessData()
{
    static int Add(int a, int b) => a + b;
    int result = Add(5, 3);
}
This ensures the function relies only on its parameters and static members. Generic local functions, supporting type parameters for greater flexibility, were introduced in . For example:
csharp
public void Process<T>(T item) where T : IComparable<T>
{
    bool IsGreater<U>(U value) where U : T => item.CompareTo(value) > 0;
    // Usage within the method, e.g., if (IsGreater(someValue)) { ... }
}
Attributes can apply to local functions since C# 9.0, including on generic parameters and the function itself, aligning with broader capabilities. Local functions also handle exceptions more predictably in iterators and async contexts by surfacing them immediately rather than deferring via delegates.

Asynchronous and Advanced Syntax

Async-Await Pattern

The async-await pattern in C# enables asynchronous programming by allowing methods to perform non-blocking operations, improving responsiveness in applications such as or server-side code. Introduced in C# 5.0 with .NET Framework 4.5, this pattern uses the async modifier to declare asynchronous methods and the await keyword to suspend execution until an asynchronous operation completes, without blocking the calling thread. An async method is declared by prefixing the method signature with async and typically returns a Task or Task<T> to represent the asynchronous . For example, the following simulates a delay and returns an integer:
csharp
public async Task<int> GetDataAsync()
{
    await Task.Delay(100);  // Non-blocking delay
    return 42;
}
This declaration allows the to use await internally and enables the to generate a state machine for handling continuations. The await expression operates on an awaitable type, such as Task, suspending the until the awaited completes or faults, then resuming with the result. Awaitables must implement INotifyCompletion or ICriticalNotifyCompletion for proper integration with the async context. The Task class represents an asynchronous operation, while Task<T> provides a result of type T. For performance-sensitive scenarios where frequent synchronous completions occur, ValueTask<T> can be used as a lightweight, allocation-free alternative, wrapping either a Task or a synchronous result. Introduced in .NET Core 2.1, ValueTask reduces garbage collection pressure in high-throughput code but requires careful handling to avoid multiple awaits on the same instance. Async lambdas extend this pattern to functions, declared with async before the parameter list, enabling asynchronous processing in delegates or queries. For instance:
csharp
var asyncLambda = async (int x) => await Task.FromResult(x * 2);
Async iterators, added in C# 8.0, allow methods to values asynchronously using yield return within an async iterator that returns IAsyncEnumerable<T>. This supports from asynchronous sources, such as responses, with consumption via await foreach. In C# 13 (November 2024), async methods gained support for ref locals and unsafe contexts, provided these do not cross await boundaries or interact with yield in iterators. This enables the use of ref structs like Span<T> in async code for better performance in scenarios involving memory manipulation, such as or buffering. For example:
csharp
public async Task ProcessSpanAsync(Span<byte> buffer)
{
    unsafe
    {
        fixed (byte* ptr = buffer)
        {
            // Process pointer synchronously
            await Task.Delay(100);  // ref/unsafe do not cross await
        }
    }
}
```[](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-13#ref-and-unsafe-in-iterators-and-async-methods)

Cancellation is integrated via `CancellationToken`, passed as a parameter to async methods to signal cancellation requests cooperatively. Methods like `Task.Delay` and HTTP clients check the token, throwing `OperationCanceledException` if canceled. For example:

```csharp
public async Task ProcessAsync(CancellationToken cancellationToken)
{
    await Task.Delay(1000, cancellationToken);
}
This ensures resources are released promptly and prevents unnecessary computation.

Pattern Matching and Deconstruction

Pattern matching in C# provides a concise way to inspect and extract data from expressions based on their structure, type, or properties, enhancing code readability and reducing boilerplate compared to traditional type checks and casts. Introduced in C# 7.0, it allows testing an expression against patterns using the is operator or switch expressions, with subsequent enhancements in later versions adding support for relational comparisons, properties, lists, and . The is pattern, debuted in C# 7.0, enables type testing combined with declaration in a single expression, such as if (obj is string s), which checks if obj is a and assigns it to s if true, eliminating the need for separate . In C# 9.0, this expanded to relational patterns, allowing comparisons like if (value is > 0 and < 10), which verifies if value falls within a numeric range without explicit conditional logic. These patterns support logical combinators such as and, or, and not for more complex conditions. Switch expressions, introduced in C# 8.0, integrate pattern matching into control flow by allowing cases to match structural patterns, for example, case string { Length: > 5 } to handle strings longer than five characters. This syntax promotes declarative matching over imperative checks and can be used in switch statements for exhaustive analysis of inputs. Deconstruction, also from C# 7.0, breaks down objects or tuples into individual variables using syntax like (int x, int y) = point;, where point must define a Deconstruct method to unpack its components. This feature supports "into" declarations in queries or foreach loops for extracting elements, and it works with tuples natively, assigning elements to variables or discards (_) to ignore parts. Property patterns, available since C# 8.0, match based on object properties, such as case Person { Age: > 18 }, verifying the Age property exceeds 18 without full deconstruction. C# 11 introduced list patterns for collections, enabling matches like is [1, .., 3] to check if a list starts with 1 and ends with 3, using .. for variable-length slices and discards for unspecified elements. Additionally, C# 11 extended pattern matching to Span<char> and ReadOnlySpan<char> against constant strings, allowing efficient, allocation-free checks like if (span is "hello") for substring matching in memory buffers. Recursive patterns, starting in C# 8.0, allow nesting patterns within others, such as matching a tree structure with case Node(int value, Node left, Node right): to recursively inspect subnodes. This capability, extended in C# 11 to include list and positional patterns, facilitates handling of hierarchical data like JSON or ASTs efficiently.

Attributes and Preprocessor Directives

In C#, attributes provide a declarative for adding to code elements such as classes, methods, , and parameters, enabling and behaviors without altering the core functionality. Attributes are applied using square brackets immediately preceding the target declaration, allowing multiple attributes in a single section separated by commas. For instance, the built-in Obsolete attribute marks an element as deprecated, generating a warning or error upon usage to discourage its invocation. The Obsolete attribute accepts a message string and an optional boolean flag to escalate to an error, as shown in the following example:
csharp
[System.Obsolete("This method is obsolete; use ProcessNew instead.", true)]
public void ProcessOld() { }
This syntax enforces deprecation at compile time, promoting code evolution while maintaining backward compatibility. Custom attributes are defined by creating classes that inherit from System.Attribute, specifying valid targets via the AttributeUsage attribute, which restricts application to elements like classes or methods using the AttributeTargets enum. These classes support positional parameters through constructors and named parameters via public properties or fields. C# 11 (November 2022) introduced generic attributes, allowing attribute classes to be generic with type parameters and constraints, enabling stronger typing for metadata. For example:
csharp
[AttributeUsage(AttributeTargets.Property)]
public class RangeAttribute<T> : Attribute where T : struct, IComparable<T>
{
    public RangeAttribute(T min, T max) { /* ... */ }
}

// Usage
[Range(0, 100)]
public int Value { get; set; }
This feature permits attributes to enforce type-specific validation at compile time. In C# 12 (November 2023), the ExperimentalAttribute was added to mark features or APIs as experimental, issuing a warning (CS8772) when used, to signal potential instability. It can be applied to types, members, or parameters:
csharp
[System.Diagnostics.CodeAnalysis.Experimental("Preview feature")]
public void ExperimentalMethod() { }
This aids in gradual rollout of new syntax or libraries. Attribute usage includes specifying targets explicitly with colons (e.g., [Help: "url"] for a method) and passing parameters in either positional or named forms for flexibility. For example, a custom HelpAttribute might be defined and applied as follows:
csharp
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class HelpAttribute : System.Attribute
{
    public string Url { get; }
    public string Topic { get; set; }

    public HelpAttribute(string url) => Url = url;
}

// Usage
[Help("https://example.com/docs", Topic = "Overview")]
public class DocumentationClass { }
This allows metadata like URLs or categories to be attached for tools like documentation generators or serializers. C# preprocessor directives facilitate conditional compilation, code organization, and warning management by processing source code before compilation, without executing as runtime code. The #define directive establishes conditional compilation symbols at the file level, enabling environment-specific builds without altering the codebase. Symbols like DEBUG are predefined by the compiler when enabled via project settings, allowing code inclusion or exclusion based on build configurations. The #if, #elif, #else, and #endif directives evaluate these symbols logically (supporting !, ==, !=, &&, ||) to include or skip blocks conditionally. An example demonstrates debug-only output:
csharp
#define VERBOSE
#if VERBOSE || DEBUG
    Console.WriteLine("Debug information");
#elif RELEASE
    // Release-specific code
#else
    // Default fallback
#endif
This mechanism supports multi-target frameworks, such as including .NET-specific APIs only when symbols like NET8_0 are defined. The #region and #endregion directives outline collapsible sections in for better code navigation, without affecting compilation. For instance:
csharp
#region Public Methods
public void Method1() { }
public void Method2() { }
#endregion
The #pragma warning directive suppresses or restores specific compiler warnings by ID, useful for unavoidable issues like unused variables in generated code. Example:
csharp
#pragma warning disable CS0168 // Variable declared but never used
int unused = 42;
#pragma warning restore CS0168
Conditional compilation symbols integrate with attributes like Conditional, which skips method calls unless the symbol is defined, enhancing syntax for debug tracing without runtime overhead.

Comments and Documentation

C# supports comments to annotate code without affecting compilation. Single-line comments begin with // and extend to the end of the line, allowing developers to add brief notes or explanations directly after code statements. For instance:
csharp
[int](/page/INT) x = 5; // This initializes x to five
Multi-line comments use /* to start and */ to end, enclosing blocks of text across multiple lines, which is useful for temporarily disabling code sections or providing longer descriptions. An example is:
csharp
/* This is a multi-line comment
   that spans several lines.
   It can include explanatory text. */
XML documentation comments, prefixed with ///, enable structured integrated into the , using XML tags to describe like classes, methods, and . These comments support recommended tags such as <summary> for overall descriptions, <param name="parameterName"> for parameter details, and <returns> for return value information. For example:
csharp
/// <summary>
/// Calculates the sum of two integers.
/// </summary>
/// <param name="a">The first integer.</param>
/// <param name="b">The second integer.</param>
/// <returns>The sum of a and b.</returns>
public int Add(int a, int b)
{
    return a + b;
}
The C# compiler can extract these XML comments into a separate XML file during build, which tools like IntelliSense or documentation generators use to provide API references. This is enabled via project settings like <GenerateDocumentationFile>true</GenerateDocumentationFile> in MSBuild. Starting with , XML documentation comments can incorporate raw interpolated s, allowing more readable and flexible formatting within tags by using triple quotes (""") combined with interpolation markers. For instance, a <summary> tag might use $""" to embed dynamic elements while preserving structure. This enhancement applies standard features to , improving maintainability for complex descriptions.

References

  1. [1]
    Introduction - C# language specification - Microsoft Learn
    This introduction provides an overview of the C# language and its design goals. Note: This chapter has been removed from the official specification in ...
  2. [2]
    Overview - A tour of C# | Microsoft Learn
    C# syntax is familiar if you used C, C++, JavaScript, TypeScript, or Java. Like C and C++, semi-colons ( ; ) define the end of statements. C# identifiers are ...Beginner C# tutorials · Annotated C# strategy · Tips for Java Developers · JavaScript
  3. [3]
    General Structure of a Program - C# | Microsoft Learn
    For more information, see Basic concepts in the C# Language Specification. The language specification is the definitive source for C# syntax and usage.C# language specification · Main() and command-line... · Introduction to classes
  4. [4]
    Basic concepts - C# language specification - Microsoft Learn
    In the example below, the local variable y is in scope within the switch section for the default case, despite the declaration appearing in the switch section ...
  5. [5]
  6. [6]
    C# identifier naming rules and conventions - Microsoft Learn
    C# identifiers must start with a letter or underscore. PascalCase is used for types, namespaces, and public members. CamelCase is used for local variables and ...Naming Conventions · Pascal Case · Camel Case
  7. [7]
  8. [8]
    C# Keywords and contextual keywords - C# reference
    C# keywords are reserved identifiers with special meanings, while contextual keywords have special meanings in limited contexts and can be used as identifiers ...When (filter condition) · Extern alias · Microsoft Ignite · The with expression
  9. [9]
    Lexical structure - C# language specification
    ### Summary of C# Keywords from https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure#644-keywords
  10. [10]
    The history of C# | Microsoft Learn
    The major feature was the introduction of the dynamic keyword. The dynamic keyword introduced into C# version 4.0 the ability to override the compiler on ...What's new in C# 11 · Relationships between... · Local functions
  11. [11]
  12. [12]
    6 Lexical structure - C# language specification - Microsoft Learn
    The prefix “ @ ” enables the use of keywords as identifiers, which is useful when interfacing with other programming languages. The character @ is not actually ...
  13. [13]
  14. [14]
    bool type - C# reference - Microsoft Learn
    Jan 25, 2022 · The bool type keyword is an alias for the .NET System.Boolean structure type that represents a Boolean value, which can be either true or false.Literals · Three-valued Boolean logic
  15. [15]
  16. [16]
    Integral numeric types (C# reference) - Microsoft Learn
    Sep 29, 2022 · Literals are interpreted as positive values. For example, the literal 0xFF_FF_FF_FF represents the number 4294967295 of the uint type ...
  17. [17]
  18. [18]
    Floating-point numeric types - C# reference - Microsoft Learn
    Sep 29, 2022 · The floating-point numeric types represent real numbers. All floating-point numeric types are value types. They are also simple types and can be initialized ...
  19. [19]
  20. [20]
    The char type - C# reference | Microsoft Learn
    Feb 28, 2025 · The char type keyword is an alias for the .NET System.Char structure type that represents a Unicode UTF-16 code unit, typically a UTF-16 character.
  21. [21]
  22. [22]
    Verbatim text and strings - @ - C# reference - Microsoft Learn
    Mar 21, 2023 · The `@` character in C# is a verbatim identifier used to create verbatim string literals, as identifiers, and to distinguish attributes.
  23. [23]
    string interpolation - format string output - C# reference
    String interpolation in C# uses the $ character to identify a string literal that might contain expressions, which are replaced by their string representations.
  24. [24]
    What's new in C# 11 - Microsoft Learn
    Mar 15, 2024 · In this article · Raw string literals · Generic math support · Generic attributes · UTF-8 string literals · Newlines in string interpolation ...
  25. [25]
  26. [26]
    null keyword - C# reference - Microsoft Learn
    Mar 30, 2024 · The null keyword is a literal that represents a null reference, one that does not refer to any object. null is the default value of reference-type variables.
  27. [27]
    What's new in C# 12 - Microsoft Learn
    Jun 4, 2024 · You can learn more about collection expressions in the language reference article on collection expressions or the feature specification.Primary constructors · Collection expressions
  28. [28]
    Collection expressions - C# language reference - Microsoft Learn
    Mar 12, 2024 · Collection expressions convert to many collection types. You can write literal values, expressions, or other collections to create a new ...
  29. [29]
    Declaration statements - C# reference - Microsoft Learn
    Jun 21, 2023 · Local variables can be explicitly or implicitly typed. A declaration statement can also include initialization of a variable's value.
  30. [30]
    Implicitly typed local variables - C# | Microsoft Learn
    Mar 13, 2023 · Local variables can be declared without giving an explicit type. The var keyword instructs the compiler to infer the type of the variable from the expression.
  31. [31]
    Target-typed new expressions - C# feature specifications
    Jun 23, 2023 · This feature specification describes the enhancements to 'new' where the target type can be inferred from the variable declaration.
  32. [32]
    The const keyword - C# reference | Microsoft Learn
    Also, although a const field is a compile-time constant, the readonly field can be used for run-time constants, as in this line: public static readonly uint ...
  33. [33]
    readonly keyword - C# reference - Microsoft Learn
    readonly indicates that assignment to the field can only occur as part of the declaration or in a constructor in the same class.
  34. [34]
    Variables - C# language specification - Microsoft Learn
    C# variables are storage locations with types. Categories include static, instance, array, value, input, reference, output, and local variables.
  35. [35]
  36. [36]
  37. [37]
    Statements - C# language specification - Microsoft Learn
    Implicitly typed declarations contain the contextual keyword (§6.4. 4) var resulting in a syntactic ambiguity between the three categories which is resolved as ...
  38. [38]
    Expression-bodied members - C# - Microsoft Learn
    Learn about expression-bodied members. See code examples that use expression body definition for properties, constructors, finalizers, and more.Methods · Read-only properties
  39. [39]
    Organizing types in namespaces - C# - Microsoft Learn
    Namespaces in C# organize types, control scope of class/method names, and help organize large code projects. The using directive helps avoid specifying the ...
  40. [40]
    The namespace keyword - C# reference - Microsoft Learn
    The `namespace` keyword declares a scope for related objects, organizing code and creating unique types. A default global namespace is also present.
  41. [41]
    File scoped namespaces - C# feature specifications - Microsoft Learn
    Jun 23, 2023 · File scoped namespaces use a less verbose format for the typical case of files containing only one namespace.Summary · Motivation
  42. [42]
    The using directive: Import types from a namespace - C# reference
    The using directive allows you to use types from a namespace without specifying the full namespace, but it doesn't give access to nested namespaces.
  43. [43]
    Global using directive - C# feature specifications - Microsoft Learn
    Mar 23, 2022 · This feature specification describes global using directives, which add a using directive to every file compiled as part of an assembly.§7.8 Namespace And Type... · Simple Names §12.8. 4 · Global Using Namespace...
  44. [44]
    Namespaces - C# language specification - Microsoft Learn
    Sep 12, 2025 · A using directive facilitates the use of namespaces and types defined in other namespaces. Using directives impact the name resolution process ...
  45. [45]
    Main() and command-line arguments - C#
    ### Summary: Main Method and Command-Line Arguments in C#
  46. [46]
    Methods (C# Programming Guide) - Microsoft Learn
    Jun 20, 2023 · A method is a code block that contains a series of statements. A program causes the statements to be executed by calling the method and specifying any required ...Method Signatures · Return Values · Async Methods
  47. [47]
    Top-level statements - programs without Main methods - C#
    You can write a Main method explicitly, but it can't function as an entry point. The compiler issues the following warning: CS7022 The entry point of the ...
  48. [48]
    Value types - C# reference - Microsoft Learn
    Oct 25, 2024 · Value types and reference types are the two main categories of C# types. A variable of a value type contains an instance of the type.Kinds of value types and type... · Built-in value typesMissing: predefined | Show results with:predefined
  49. [49]
    Built-in types - C# reference - Microsoft Learn
    The C# language defines implicit conversions from array types and the string type to Span<T> and ReadOnlySpan<T> . These conversions integrate Span types into ...Value types · Integral numeric types · Bool · Char
  50. [50]
    Structure types - C# reference - Microsoft Learn
    A structure type (or struct type) is a value type that can encapsulate data and related functionality. You use the struct keyword to define a structure type.readonly struct · readonly instance members
  51. [51]
    Structs - C# language specification - Microsoft Learn
    This chapter defines struct declarations. In many cases, the descriptions are defined using the differences between classes and structs.Missing: predefined | Show results with:predefined
  52. [52]
    Enumeration types - C# reference - Microsoft Learn
    An enumeration type (or enum type) is a value type defined by a set of named constants of the underlying integral numeric type.Implicit conversions from zero · Enumeration types as bit flags
  53. [53]
    Enums - C# language specification - Microsoft Learn
    This chapter defines the enum types in C#. Enums create a set of named constants and are represented by an underlying integral set of values.General · Enum declarations
  54. [54]
    Tuple types - C# reference - Microsoft Learn
    May 17, 2023 · Tuple types are value types; tuple elements are public fields. That makes tuples mutable value types. You can define tuples with an arbitrary ...Use cases of tuples · Tuple field names
  55. [55]
    ref struct types - C# reference - Microsoft Learn
    You can use the ref modifier in the declaration of a structure type. Instances of a ref struct type are allocated on the stack and can't escape to the managed ...<|separator|>
  56. [56]
    Reference types - C# reference - Microsoft Learn
    Mar 30, 2024 · There are two kinds of types in C#: reference types and value types. Variables of reference types store references to their data (objects).
  57. [57]
    Nullable reference types - C# - Microsoft Learn
    Nullable reference types are a group of features that minimize the likelihood that your code causes the runtime to throw System.NullReferenceException.
  58. [58]
    The array reference type - C# reference | Microsoft Learn
    Store multiple variables of the same type in an array data structure in C#. Declare an array by specifying a type or specify Object to store any type.
  59. [59]
    Classes in the C# type system. - Microsoft Learn
    The name of the class follows the class keyword. The name of the class must be a valid C# identifier name. The remainder of the definition is the class body, ...Creating Objects · Constructors And... · Class Inheritance<|control11|><|separator|>
  60. [60]
    The new operator creates a new instance of a type - Microsoft Learn
    Nov 14, 2023 · The new operator creates a new instance of a type. You can also use the new keyword as a member declaration modifier or a generic type constraint.Constructor invocation · Array creation
  61. [61]
    Built-in reference types - C# - Microsoft Learn
    In the unified type system of C#, all types, predefined and user-defined, reference types and value types, inherit directly or indirectly from System.Object.
  62. [62]
    How to declare, instantiate, and use a delegate - C# - Microsoft Learn
    Dec 22, 2024 · You can declare delegates using any of the following methods: Declare a delegate type and declare a method with a matching signature.
  63. [63]
  64. [64]
    Conversions - C# language specification - Microsoft Learn
    This chapter covers the possible conversions from one type to another in C#. Builtin conversions, user defined conversions, implicit and explicit ...
  65. [65]
    Built-in numeric conversions - C# reference - Microsoft Learn
    Jan 31, 2023 · C# provides a set of integral and floating-point numeric types. There exists a conversion between any two numeric types, either implicit or explicit.
  66. [66]
    Nullable value types - C# reference - Microsoft Learn
    Apr 7, 2023 · A nullable value type (T?) represents all values of its underlying type T and an additional null value, used when a value can be undefined.
  67. [67]
    The checked and unchecked statements (C# reference)
    Nov 1, 2024 · The checked and unchecked statements specify the overflow-checking context for integral-type arithmetic operations and conversions.Numeric types and overflow... · Operations affected by the...
  68. [68]
    Boxing and Unboxing (C# Programming Guide) - Microsoft Learn
    Boxing is the process of converting a value type to the type object or to any interface type implemented by this value type. When the common language ...Performance · Boxing
  69. [69]
    Casting and type conversions - C# - Microsoft Learn
    Learn about casting and type conversions, such as implicit, explicit (casts), and user-defined conversions.Implicit conversion operator · Type-testing operators · Boxing and Unboxing - C
  70. [70]
    User-defined explicit and implicit conversion operators - C# reference
    Learn how to define custom implicit and explicit type conversions in C#. The operators provide the functionality for casting an object to a new type.
  71. [71]
    Arithmetic operators - C# reference - Microsoft Learn
    The checked operator is called in a checked context; the operator without the checked modifier is called in an unchecked context. If you only provide the ...
  72. [72]
  73. [73]
    Comparison operators (C# reference) - Microsoft Learn
    Apr 7, 2023 · C# comparison operators check the order of values. The operators `>`, `<`, `>=`, `<=` compare the order of values. They determine if a value ...
  74. [74]
    Boolean logical operators - AND, OR, NOT, XOR - Microsoft Learn
    The operators include the unary logical negation ( ! ), binary logical AND ( & ), OR ( | ), and exclusive OR ( ^ ), and the binary conditional logical AND ( && ) ...Missing: relational | Show results with:relational
  75. [75]
    Bitwise and shift operators - C# reference | Microsoft Learn
    The bitwise and shift operators include unary bitwise complement, binary left and right shift, unsigned right shift, and the binary logical AND, OR, and ...
  76. [76]
    ?: operator - the ternary conditional operator - C# reference
    ### Summary of Conditional Operator `?:` in C#
  77. [77]
    List all operators and expression - C# reference | Microsoft Learn
    Mar 8, 2023 · The simplest C# expressions are literals (for example, integer and real numbers) and names of variables. You can combine them into complex expressions by using ...
  78. [78]
    null-coalescing operators - C# reference | Microsoft Learn
    The `??` and `??=` operators are the C# null-coalescing operators. They return the value of the left-hand operand if it isn't null.
  79. [79]
  80. [80]
    Member access and null-conditional operators and expressions
    C# operators that you use to access type members or null-conditionally access type members. These operators include the dot operator - `.
  81. [81]
    Pointer related operators - C# reference - Microsoft Learn
    Apr 11, 2023 · Arithmetic operators + , - , ++ , and --; Comparison operators == , != , < , > , <= , and >=.<|control11|><|separator|>
  82. [82]
  83. [83]
    Member access and null-conditional operators and expressions: - C# reference
    ### Index Operator `[]` and Null-Conditional Indexing `?[]` Summary
  84. [84]
    Indexers - C# | Microsoft Learn
    Indexers in C# allow class or struct instances to be indexed like arrays. You can set or get the indexed value without specifying a type or instance member.
  85. [85]
    delegate operator - Create an anonymous method that can be ...
    Mar 30, 2024 · The delegate operator creates an anonymous method that can be converted to a delegate type. An anonymous method can be converted to types such as System.Action ...
  86. [86]
    The multiple uses of the `ref` keyword - C# reference - Microsoft Learn
    Aug 1, 2024 · The `ref` keyword is used to pass arguments by reference, return values by reference, declare reference variables, and declare ref structs and  ...Method parameters · Jump statements - break... · Declaration statements
  87. [87]
    out keyword - C# reference
    ### Summary of `out` Keyword in C# (from https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/out)
  88. [88]
    Discards - unassigned discardable variables - C# - Microsoft Learn
    Discards are placeholder variables, equivalent to unassigned variables, indicated by the underscore ( _), and are intentionally unused in code.Tuple and object deconstruction · Pattern matching with switch
  89. [89]
    Discard - _ - C# reference - Microsoft Learn
    Dec 21, 2024 · The _ character serves as a discard, which is a placeholder for an unused variable. There are two uses for the discard token: To declare an unused variable.
  90. [90]
    if and switch statements - select a code path to execute - C# reference
    The if, if-else and switch statements select statements to execute from many possible paths based on the value of an expression.Microsoft Ignite · Switch expression · Jump statements - break...
  91. [91]
    Pattern matching using the is and switch expressions. - C# reference
    You use the is expression, the switch statement and the switch expression to match an input expression against any number of characteristics.Microsoft Ignite · If and switch statements · The `is` operator<|separator|>
  92. [92]
    Evaluate a pattern match expression using the `switch` expression
    Dec 2, 2022 · You use the switch expression to evaluate a single expression from a list of candidate expressions based on a pattern match with an input expression.Missing: declaration | Show results with:declaration
  93. [93]
    Iteration statements -for, foreach, do, and while - C# reference
    Nov 14, 2023 · The iteration statements repeatedly execute a statement or a block of statements. The for statement executes its body while a specified Boolean expression ...
  94. [94]
  95. [95]
  96. [96]
  97. [97]
  98. [98]
    Explore ranges of data using indices and ranges - C# - Microsoft Learn
    Nov 14, 2023 · This advanced tutorial teaches you to explore data using indices and ranges to examine a continuous range of a sequential data set.
  99. [99]
    Jump statements - break, continue, return, and goto - C# reference
    Mar 14, 2023 · The return value must have a lifetime that extends beyond the execution of the method. In other words, it can't be a local variable in the ...<|separator|>
  100. [100]
    Exception-handling statements - throw and try, catch, finally - C# ...
    execution of a jump statement (that is, return , break , continue , or goto ), or; propagation of an exception out of the try block. The following example ...
  101. [101]
    using statement - ensure the correct use of disposable objects
    The using statement ensures that a disposable instance is disposed even if an exception occurs within the block of the using statement.
  102. [102]
    class keyword - C# reference | Microsoft Learn
    Mar 30, 2024 · Classes are declared using the keyword class , as shown in the following example: class TestClass { // Methods, properties, fields, events, ...
  103. [103]
    Choosing Between Class and Struct - Framework Design Guidelines
    Changes to an instance of a reference type affect all references pointing to the instance. Value type instances are copied when they are passed by value. When ...
  104. [104]
    Declare C# primary constructors – classes, structs | Microsoft Learn
    C# 12 introduces primary constructors, which provide a concise syntax to declare constructors whose parameters are available anywhere in the body of the type.
  105. [105]
    Primary constructors - C# feature specifications - Microsoft Learn
    Primary constructors put the parameters of one constructor in scope for the whole class or struct to be used for initialization or directly as object state.Summary · Motivation
  106. [106]
    Records - C# reference - Microsoft Learn
    A record struct declares a value type. Positional properties are immutable in a record class and a readonly record struct . They're mutable in a record struct .
  107. [107]
    Record types - C# - Microsoft Learn
    A record in C# is a class or struct that provides special syntax and behavior for working with data models.Records · Interfaces · Equality Comparisons - C
  108. [108]
    The init keyword - init only properties - C# reference - Microsoft Learn
    The init keyword defines an accessor method in a property or indexer. An init-only setter assigns a value to the property or the indexer element only during ...
  109. [109]
    Using Properties - C# - Microsoft Learn
    These access modifiers define how users of the class can access the property. The get and set accessors for the same property can have different access ...
  110. [110]
    Interfaces - define behavior for multiple types - C# | Microsoft Learn
    An interface in C# contains definitions for a group of related functionalities that a non-abstract class or a struct must implement.
  111. [111]
    interface keyword - C# reference - Microsoft Learn
    Oct 4, 2025 · An interface defines a contract. Any class, record or struct that implements that contract must provide an implementation of the members defined in the ...
  112. [112]
    Safely update interfaces using default interface methods - C#
    Default interface implementations enable developers to upgrade an interface while still enabling any implementors to override that implementation.
  113. [113]
    Explicit Interface Implementation - C# - Microsoft Learn
    Sep 29, 2022 · An explicit interface implementation is a class member that is only called through the specified interface.
  114. [114]
    Interfaces - C# language specification | Microsoft Learn
    Oct 23, 2025 · This chapter defines interfaces. This includes interface declarations, implementing interfaces, and explicit interface implementation.General · Interface declarations
  115. [115]
    Objected oriented programming - inheritance - C# | Microsoft Learn
    Inheritance in C# enables you to create new classes that reuse, extend, and modify the behavior defined in other classes.
  116. [116]
    Abstract and Sealed Classes and Class Members - C# | Microsoft ...
    Oct 27, 2021 · The abstract keyword enables you to create classes and class members that are incomplete and must be implemented in a derived class.
  117. [117]
    Classes - C# language specification - Microsoft Learn
    Sep 12, 2025 · A conversion operator declaration that includes the implicit keyword introduces a user-defined implicit conversion. Implicit conversions can ...
  118. [118]
    abstract keyword - C# reference - Microsoft Learn
    Use the abstract modifier in a class declaration to indicate that a class is intended only to be a base class of other classes, not instantiated on its own.Example 1 - Abstract class with... · Example 2
  119. [119]
    Classes, structs, and records - C# | Microsoft Learn
    In C#, the definition of a type—a class, struct, or record—is like a blueprint that specifies what the type can do. An object is basically a block of memory ...Members · Objects · Object-Oriented Programming · Inheritance
  120. [120]
    Access Modifiers (C# Programming Guide) - Microsoft Learn
    If one declaration of the partial class or member doesn't include an access modifier, the other declarations can't declare an access modifier.File (C# Reference) · Internal keyword · Fields · Protected (C# Reference)
  121. [121]
    Fields - C# | Microsoft Learn
    May 26, 2023 · A read-only field can only be assigned a value during initialization or in a constructor. A static readonly field is similar to a constant, ...<|separator|>
  122. [122]
    Properties - C# | Microsoft Learn
    The accessors assign or return the result of an expression. You can implement these properties as expression-bodied members. Expression body definitions consist ...
  123. [123]
    Method parameters and modifiers - C# reference - Microsoft Learn
    Methods can store the values of parameters in fields. When parameters are passed by value, that's usually safe. Values are copied, and reference types are ...Missing: lifetime | Show results with:lifetime
  124. [124]
    Named and Optional Arguments - C# | Microsoft Learn
    Optional parameters are defined at the end of the parameter list, after any required parameters. The caller must provide arguments for all required parameters ...Named arguments · Optional arguments
  125. [125]
    Local functions - C# | Microsoft Learn
    Local functions in C# are private methods that are nested in another member and can be called from their containing member.Local function syntax · Local functions and exceptions
  126. [126]
    Constructors (C# programming guide) - Microsoft Learn
    A constructor in C# is called when a class or struct is created. Use constructors to set defaults, limit instantiation, and write flexible, easy-to-read ...Using Constructors · Instance constructors · Static ConstructorsMissing: 12 | Show results with:12
  127. [127]
    Static Constructors (C# Programming Guide) - Microsoft Learn
    Aug 2, 2024 · A static constructor is used to initialize any static data, or to perform a particular action that needs to be performed only once.
  128. [128]
    Object and Collection Initializers - C# | Microsoft Learn
    Oct 13, 2025 · Object initializers let you assign values to any accessible fields or properties of an object at creation time without having to invoke a constructor.
  129. [129]
    The `event` keyword - C# reference - Microsoft Learn
    The `event` keyword declares an event, a member that enables an object to trigger notifications. Event users attach code via event handlers.
  130. [130]
    Events (C# Programming Guide) - Microsoft Learn
    Learn about events. Events enable a class or object to notify other classes or objects when something of interest occurs.
  131. [131]
    Finalizers (C# Programming Guide) - Microsoft Learn
    Mar 13, 2023 · Finalizers (historically referred to as destructors) are used to perform any necessary final clean-up when a class instance is being collected by the garbage ...Remarks · Using finalizers to release...
  132. [132]
    Generics in .NET - Microsoft Learn
    Feb 17, 2023 · Generics are classes, structures, interfaces, and methods that have placeholders (type parameters) for one or more of the types that they store or use.Define and use generics · Advantages and...
  133. [133]
  134. [134]
    Generic Classes (C# Programming Guide) - Microsoft Learn
    Jul 9, 2022 · Generic classes encapsulate operations that are not specific to a particular data type. The most common use for generic classes is with collections.
  135. [135]
    Generic methods (C# programming guide) - Microsoft Learn
    Mar 27, 2024 · A generic method in C# is declared with type parameters. The compiler can infer the type, and type inference occurs at compile time.
  136. [136]
    where (generic type constraint) - C# reference - Microsoft Learn
    Jul 30, 2024 · The where clause in a generic definition specifies constraints on the types that are used as arguments for type parameters in a generic type, method, delegate, ...
  137. [137]
    produce the default value for any type - C# reference - Microsoft Learn
    Mar 30, 2024 · A default value expression produces the default value of a type. There are two kinds of default value expressions: the default operator call and a default ...Missing: predefined | Show results with:predefined<|control11|><|separator|>
  138. [138]
    Generic Interfaces (C# Programming Guide) - Microsoft Learn
    Learn about using generic interfaces in C#. See code examples and view other available resources.
  139. [139]
    Generic Delegates (C# Programming Guide) - Microsoft Learn
    Mar 12, 2024 · Learn how to declare, instantiate, and invoke delegates for scenarios that require dynamic method invocation, such as callback methods and ...
  140. [140]
    Covariance and Contravariance (C#) - Microsoft Learn
    Jul 30, 2022 · In C#, covariance and contravariance enable implicit reference conversion for array types, delegate types, and generic type arguments.Variance in Generic Interfaces · Using Variance in Delegates
  141. [141]
    out keyword (generic modifier) - C# reference - Microsoft Learn
    Sep 15, 2021 · Covariance enables you to use a more derived type than that specified by the generic parameter. This allows for implicit conversion of classes ...
  142. [142]
    Variance in Generic Interfaces (C#) - Microsoft Learn
    Sep 15, 2021 · Contravariance permits a method to have argument types that are less derived than that specified by the generic parameter of the interface. To ...<|control11|><|separator|>
  143. [143]
    in (Generic Modifier) - C# reference - Microsoft Learn
    Covariance and contravariance in generic type parameters are supported for reference types, but they are not supported for value types.
  144. [144]
    Variance in Delegates - C#
    ### Summary of Covariance and Contravariance in Delegates (C#)
  145. [145]
    IEnumerable<T> Interface (System.Collections.Generic)
    When you implement IEnumerable<T>, you must also implement IEnumerator<T> or, for C# only, you can use the yield keyword. Implementing IEnumerator<T> also ...Definition · Examples
  146. [146]
  147. [147]
    Iterators - C# | Microsoft Learn
    Nov 10, 2021 · An iterator method defines how to generate the objects in a sequence when requested. You use the yield return contextual keywords to define an iterator method.
  148. [148]
    yield statement - provide the next element in an iterator - C# reference
    You use the yield statement in an iterator to provide the next value or signal the end of an iteration. The yield statement has the two following forms:.<|control11|><|separator|>
  149. [149]
    Essential .NET - Custom Iterators with Yield - Microsoft Learn
    If the member uses the yield return statement, the C# compiler generates the necessary code to maintain the state of the iterator. In contrast, if the member ...Iterators and State · Another Iterator Example
  150. [150]
    Iterate through collections - C# | Microsoft Learn
    An iterator can be used to step through collections such as lists and arrays. An iterator method or get accessor performs a custom iteration over a collection.
  151. [151]
    Language Integrated Query (LINQ) - C# | Microsoft Learn
    Query expressions are written in a declarative query syntax. By using query syntax, you perform filtering, ordering, and grouping operations on data sources ...Introduction to LINQ Queries - C · Write LINQ queries · Query expression basics
  152. [152]
    Query expression basics (LINQ) - C# | Microsoft Learn
    A query expression consists of a set of clauses written in a declarative syntax similar to SQL or XQuery. Each clause in turn contains one or more C# ...
  153. [153]
    LINQ overview - .NET - Microsoft Learn
    Language-Integrated Query (LINQ) provides language-level querying capabilities, and a higher-order function API to C# and Visual BasicLanguage-level query syntax · LINQ is expressive
  154. [154]
  155. [155]
    Query keywords - C# reference - Microsoft Learn
    Aug 5, 2023 · Query keywords in C# include `from`, `where`, `select`, `group`, `into`, `orderby`, `join`, `let`, `in`, `on`, `equals`, `by`, `ascending`, and ...
  156. [156]
    Standard Query Operators Overview - C# | Microsoft Learn
    The LINQ standard query operators provide query capabilities including filtering, projection, aggregation, and sorting in C#.
  157. [157]
    Extension members - C# - Microsoft Learn
    Extension members enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type.
  158. [158]
    Extension Methods - Framework Design Guidelines - Microsoft Learn
    Extension methods are a language feature that allows static methods to be called using instance method call syntax.
  159. [159]
    The Task Asynchronous Programming (TAP) model with async and ...
    Async methods are easy to write. The async and await keywords in C# are the heart of async programming. By using those two keywords, you can use resources in . ...
  160. [160]
    async keyword - C# reference - Microsoft Learn
    Mar 22, 2023 · Use the async modifier to specify that a method, lambda expression, or anonymous method is asynchronous.
  161. [161]
    await operator - asynchronously wait for a task to complete
    Jul 1, 2024 · The await operator suspends evaluation of the enclosing async method until the asynchronous operation represented by its operand completes.
  162. [162]
    Asynchronous programming - C# | Microsoft Learn
    Jul 16, 2025 · Explore an overview of the C# language support for asynchronous programming by using async, await, Task, and Task.Async return types · Task asynchronous · 15 Classes
  163. [163]
    ValueTask<TResult> Struct (System.Threading.Tasks)
    A ValueTask<TResult> instance may only be awaited once, and consumers may not read Result until the instance has completed.
  164. [164]
    Understanding the Whys, Whats, and Whens of ValueTask - .NET Blog
    Nov 7, 2018 · But with the .NET Framework 4.5 and C# 5, Task s could simply be await ed, making it easy to consume the results of an asynchronous operation ...
  165. [165]
    Lambda expressions and anonymous functions - C# - Microsoft Learn
    Or, you can use implicitly typed variables with var declarations to define the delegate type. The compiler synthesizes the correct delegate type. For more ...<|separator|>
  166. [166]
    Iterating with Async Enumerables in C# 8 - Microsoft Learn
    Nov 1, 2019 · You can add a CancellationToken parameter to your async iterator method and annotate it with this attribute. In doing so, the compiler will ...Missing: lambdas | Show results with:lambdas<|control11|><|separator|>
  167. [167]
    Cancel async tasks after a period of time" - C# - Microsoft Learn
    Sep 8, 2023 · You can cancel an asynchronous operation after a period of time by using the CancellationTokenSource.CancelAfter method if you don't want to wait for the ...Missing: lambdas iterators
  168. [168]
    Cancellation in Managed Threads - .NET - Microsoft Learn
    Sep 1, 2022 · One cancellation token should refer to one "cancelable operation," however that operation may be implemented in your program. After the ...Operation Cancellation... · Listening And Responding To... · Listening By Registering A...Missing: lambdas | Show results with:lambdas
  169. [169]
    The `is` operator - C# reference - Microsoft Learn
    Learn about the C# `is` operator that matches an expression against a pattern. The `is` operator returns true when the expression matches the pattern.
  170. [170]
    Pattern matching changes - C# feature specifications - Microsoft Learn
    C# 9.0 pattern-matching adds type, parenthesized, conjunctive, disjunctive, negated, and relational patterns, along with new combinators like and, or, and not.Relational Patterns · Pattern Combinators · Open Issues With Proposed...
  171. [171]
    extract properties or fields from a tuple or other type - C# reference
    Dec 21, 2024 · A deconstruction expression extracts data fields from an instance of an object. Each discrete data element is written to a distinct variable.
  172. [172]
    Deconstructing tuples and other types - C# - Microsoft Learn
    A variable named "_" represents a discard. A single deconstruction operation can include multiple discards. The following example deconstructs a Person ...
  173. [173]
    Attributes - C# language specification - Microsoft Learn
    Attributes are defined through the declaration of attribute classes (§23.2), which can have positional and named parameters (§23.2.3). Attributes are attached ...General · Attribute classes
  174. [174]
  175. [175]
    Attributes interpreted by the compiler: Miscellaneous - C# reference
    Learn about attributes that affect code generated by the compiler: the Conditional, Obsolete, AttributeUsage, ModuleInitializer, and SkipLocalsInit ...
  176. [176]
  177. [177]
  178. [178]
    Preprocessor directives - C# reference - Microsoft Learn
    #if , along with the #else , #elif , #endif , #define , and #undef directives, lets you include or exclude code based on the existence of one or more symbols.
  179. [179]
  180. [180]
  181. [181]
  182. [182]
    // and /* */ - comments - C# reference | Microsoft Learn
    Dec 13, 2022 · C# supports two different forms of comments. Single line comments start with // and end at the end of that line of code. Multiline comments start with /* and ...
  183. [183]
    Generate XML API documentation comments - C# - Microsoft Learn
    You create documentation for your code by writing special comment fields indicated by triple slashes. The comment fields include XML elements that describe the ...
  184. [184]
    Recommended XML documentation tags - C# reference
    This article provides the syntax and definitions for recommended tags on types, and their members for XML documentation.
  185. [185]
  186. [186]
    Raw string literals - """ - C# reference - Microsoft Learn
    Raw string literals can contain any arbitrary text without the need for special escape sequences. You begin and end a raw string literal with a minimum of ...
  187. [187]
    Documentation comments - C# language specification
    Sep 12, 2025 · C# provides a mechanism for programmers to document their code using a comment syntax that contains XML text.D. 3 Recommended Tags · D. 4.3 Id String Examples · D. 5 An Example