C-sharp
C# (pronounced "C Sharp") is a simple, modern, general-purpose, object-oriented programming language designed by Microsoft to support robust and durable software components.[1] Developed as part of the .NET Framework initiative, it enables the creation of a wide range of applications, from desktop and web to mobile and cloud-based systems, while emphasizing type safety, garbage collection, and portability for developers familiar with languages like C and C++.[1] The language has evolved into an open-source, cross-platform standard, approved by Ecma International (ECMA-334) and the International Organization for Standardization (ISO/IEC 23270).[1]
The principal designers of C# were Anders Hejlsberg, Scott Wiltamuth, and Peter Golde, who began work on the language in 2000 under Microsoft's .NET project.[1] The first implementation was released in July 2000, with the official version 1.0 launching in January 2002 alongside .NET Framework 1.0 and Visual Studio .NET.[2] Since then, C# has seen annual major releases, incorporating features like generics (C# 2.0, 2005), LINQ and lambda expressions (C# 3.0, 2007), async/await (C# 5.0, 2012), and records with top-level statements (C# 9.0, 2020).[2] As of November 2025, the latest version is C# 14, integrated with .NET 10, which introduces enhancements for productivity, such as improved pattern matching and file-based application structures.[3]
C# supports multiple programming paradigms, including functional, declarative, and component-oriented approaches, alongside its core object-oriented foundation with classes, interfaces, and inheritance.[4] Key features include strong typing with compile-time checks, automatic memory management via garbage collection, LINQ for querying data, async programming for scalable applications, and modern constructs like pattern matching, tuples, and collection expressions.[4] It runs on the cross-platform .NET runtime, enabling development for Windows, Linux, macOS, Android, iOS, and web via tools like Blazor, with support from integrated development environments such as Visual Studio and Visual Studio Code.[5] Widely adopted by millions of developers and thousands of companies in sectors like finance, gaming, and healthcare, C# ranks among the most popular languages, powering high-performance applications that process billions of requests daily.[5]
History
Origins and design goals
C# was developed by Microsoft in the late 1990s as a modern programming language designed to leverage the emerging .NET Framework, with its first public release occurring in July 2000 alongside the framework's launch.[1] The principal designers were Anders Hejlsberg, Scott Wiltamuth, and Peter Golde, who drew significant influences from C and C++ to ensure familiarity for experienced programmers, while incorporating elements from Java through Hejlsberg's prior work on Microsoft's Visual J++, a Java development tool that faced legal challenges and was eventually deprecated.[6][1] Hejlsberg's experience with Delphi, where he served as chief architect, also shaped C#'s emphasis on component-oriented design and developer productivity.[6]
The core design goals of C# centered on creating a simple, modern, general-purpose, object-oriented language that prioritizes type safety, automatic garbage collection, and robust error handling to enhance developer productivity and software reliability.[1] It aimed to support component-oriented programming by enabling the creation of reusable, versionable software components that could evolve without breaking existing code, while providing a clean syntax that avoids the complexities of C++ such as manual memory management.[1] Additional objectives included facilitating distributed computing, source code portability across platforms, and internationalization, making it suitable for building applications in hosted and embedded environments.[1]
C# was fundamentally tied to the .NET Framework and its Common Language Runtime (CLR), now part of the broader Common Language Infrastructure (CLI), which provides the runtime environment, libraries, and execution model essential for the language's operation.[1] Early development involved prototypes influenced by internal Microsoft projects, including extensions from Visual J++ efforts, to create a safe, managed alternative to C++ tailored for the .NET ecosystem.[6] This integration with CLR ensured features like garbage collection and type safety were enforced at runtime, aligning with the goal of reducing common programming errors.[1]
Naming and early development
The name "C#" derives from the musical note C♯, selected for its brevity, branding potential, and to evoke a "sharp" advancement over predecessors like C and C++, reflecting the language's aim to provide a modern, component-oriented evolution in the C family.[1] Prior to this, the project bore the internal codename "Cool," an acronym for "C-like Object Oriented Language," which was used during initial development but abandoned for the final name to better align with marketing and design aspirations.[7]
Development of C# commenced in 2000 at Microsoft, when Anders Hejlsberg, previously known for Turbo Pascal and Delphi at Borland, assembled a team to design a new language tailored for the emerging .NET platform.[2] The first public preview occurred at Microsoft's Professional Developers Conference (PDC) in July 2000, where Hejlsberg demonstrated the language's core features to developers, generating immediate interest for its productivity enhancements over existing tools.[8] C# 1.0 was officially released on January 15, 2002, coinciding with Visual Studio .NET 2002 and the .NET Framework 1.0, marking its integration into enterprise development workflows.[2]
The initial C# compiler, csc.exe, was tightly integrated with Visual Studio .NET, offering syntax highlighting, IntelliSense, debugging, and project management tools that streamlined compilation and deployment for Windows-based applications.[2] This IDE support was crucial for early users transitioning from languages like C++ or Java, enabling faster iteration despite the novelty of the .NET runtime.
Early adoption of C# encountered hurdles, including perceptions of it as a Java derivative amid antitrust scrutiny on Microsoft, and resistance from developers accustomed to unmanaged code in C++.[9] Community feedback praised its type safety and simplicity but noted initial performance overheads in the managed environment and limited cross-platform support, prompting rapid iterations.[10] Standardization by ECMA International in April 2002 further bolstered credibility and encouraged wider experimentation.[11]
Version history
C# 1.0 was released in January 2002 alongside .NET Framework 1.0, introducing core object-oriented programming constructs such as classes, interfaces, and inheritance, along with delegates and events for event handling.[2]
C# 2.0 arrived in November 2005 with .NET Framework 2.0, adding generics for type-safe collections, anonymous methods for inline delegates, and iterators using the yield keyword to simplify sequence generation.[2]
The release of C# 3.0 in November 2007, tied to .NET Framework 3.5, brought Language Integrated Query (LINQ) for querying data sources, lambda expressions for concise function definitions, and extension methods to augment existing types.[2]
C# 4.0 launched in April 2010 with .NET Framework 4.0, featuring dynamic typing via the dynamic keyword for late binding, named and optional parameters to improve method calls, and support for covariance and contravariance in generics.[2]
In August 2012, C# 5.0 debuted with .NET Framework 4.5, primarily introducing the async and await keywords to simplify asynchronous programming without blocking threads.[2]
C# 6.0 was released in July 2015 alongside .NET Framework 4.6, incorporating string interpolation for formatted strings, null-conditional operators (?.) to avoid null reference exceptions, and expression-bodied members for concise property and method definitions.[2]
C# 7.0 through 7.3 spanned 2017 to 2018, aligned with .NET Core 1.0 to 2.2 and .NET Framework 4.7 to 4.7.2; key additions included tuples for lightweight data structures, pattern matching for deconstructing data, local functions for scoped helpers, and enhancements like async entry points and readonly references.[2]
C# 8.0 emerged in September 2019 with .NET Core 3.0, enabling nullable reference types to help prevent null errors, default interface methods for evolving APIs, and async streams for asynchronous iteration.[2]
Released in November 2020 with .NET 5, C# 9.0 introduced records as immutable data types, init-only properties for set-once setters, and top-level statements to reduce boilerplate in simple programs.[2]
C# 10.0 followed in November 2021 with .NET 6, offering global using directives for broader namespace imports, file-scoped namespaces to limit scope, and record structs combining value types with records.[2]
In November 2022, C# 11.0 accompanied .NET 7, adding raw string literals for multiline strings without escapes, generic attributes for type-parameterized metadata, and required members to enforce initialization.[2]
C# 12.0 was released in November 2023 with .NET 8, featuring primary constructors for direct parameter use in classes, collection expressions for concise array and list initialization, and inline arrays for performance-critical fixed-size buffers.[2]
C# 13.0 launched in November 2024 alongside .NET 9, enhancing params collections for flexible array parameters, introducing a new lock type for improved concurrency, and supporting implicit indexer access for easier collection manipulation.[2]
The latest version, C# 14.0, was released in November 2025 with .NET 10, introducing extension members for static extensions on interfaces, the field keyword to simplify property implementations, implicit conversions for Span types, and null-conditional assignments.[3]
Syntax and basic features
Basic syntax elements
C# programs follow a structured format that organizes code into namespaces, classes, and methods. A typical program begins with using directives, which import namespaces to access predefined types and members without fully qualifying their names, such as using System;.[12] Namespaces group related types and prevent naming conflicts, declared with the syntax namespace Identifier { ... }.[12] The core logic resides within a class, defined as class ClassName { ... }, and execution starts from the Main method, which is static and returns void, typically static void Main(string[] args) { ... }.[12] In modern C# versions, top-level statements allow omitting explicit class and Main declarations for simpler console applications.[12]
Comments in C# document code without affecting compilation. Single-line comments begin with // and extend to the line's end, as in // This is a comment.[13] Multi-line comments enclose text between /* and */, spanning multiple lines if needed, for example:
csharp
/* This is a multi-line
comment. */
/* This is a multi-line
comment. */
Documentation comments use /// to generate XML-formatted output for tools like IntelliSense.[13]
Variables store data during execution and are declared with a type, name, and optional initializer, such as [int](/page/INT) count = 0;.[14] Multiple variables of the same type can be declared comma-separated: [int](/page/INT) a, b;. Constants, declared with const, hold immutable values known at compile time and must be initialized immediately, like const [double](/page/Double) Pi = 3.14159;.[14] Only built-in types such as integers, floating-point, boolean, and string support constants.[15]
Operators perform operations on operands. Arithmetic operators include addition (+), subtraction (-), multiplication (*), and division (/), applied to numeric types; for instance, int sum = 5 + 3; yields 8.[16] Logical operators evaluate boolean expressions: conditional AND (&&) returns true if both operands are true, as in (x > 0) && (y > 0), while conditional OR (||) returns true if at least one is true.[17] The assignment operator (=) stores a value in a variable, e.g., x = 10;.[18]
Strings represent sequences of characters, declared as string message = "Hello";. String literals are enclosed in double quotes and support escape sequences for special characters, such as \n for newline or \" for quotes inside strings.[19] Verbatim string literals, prefixed with @, treat backslashes literally without escape processing, useful for paths like @"C:\folder\file.txt".[20]
Preprocessor directives control compilation conditionally. The #define directive defines a symbol at the file's top, e.g., #define DEBUG, while #if includes code only if the symbol is defined, paired with #endif, as in:
csharp
#if DEBUG
Console.WriteLine("Debug mode");
#endif
#if DEBUG
Console.WriteLine("Debug mode");
#endif
[21] These directives enable platform-specific or debug code without runtime checks.[21]
Data types and type system
C# features a static, strongly typed type system that enforces type safety at compile time, ensuring that operations are performed only on compatible types and preventing many common programming errors. All types in C# ultimately derive from the base class System.Object, forming a unified type system that supports both value semantics and reference semantics. The language distinguishes between value types, which store their data directly, and reference types, which store references to their data on the managed heap. This design promotes memory efficiency for small, simple data while allowing complex objects to be shared and garbage-collected.[22][23]
Value types in C# include primitive types such as int (a 32-bit signed integer ranging from -2,147,483,648 to 2,147,483,647), float (a 32-bit single-precision floating-point number with approximately 7 decimal digits of precision), and bool (representing true or false values). User-defined value types encompass struct (composite types that can contain fields, methods, and properties, like a point with x and y coordinates) and enum (sets of named integer constants, such as days of the week with an underlying int type). Variables of value types hold the actual data inline on the stack or within other objects, and assignment creates a copy rather than a shared reference, which ensures isolation but requires explicit copying for large structures.[24][25][23]
Reference types, in contrast, include class (for defining objects with fields, methods, and inheritance support), interface (contracts specifying methods and properties that implementing types must provide), delegate (type-safe function pointers that reference one or more methods), arrays (collections of elements of the same type, deriving from System.Array), and string (immutable sequences of Unicode characters, aliasing System.String). These types allocate their instances on the heap, with variables holding references (pointers) to the data; assigning a reference type variable copies the reference, allowing multiple variables to point to the same object and enabling shared modifications. Null is a valid value for reference types, representing the absence of an object.[23][22]
C# supports type inference through the var keyword, which allows the compiler to deduce the type of a variable from its initializer expression, such as var number = 42; inferring an int. This feature simplifies code without sacrificing type safety, as the inferred type is fixed at compile time and treated equivalently to an explicit declaration. The compiler analyzes the expression's type, preferring more specific types over general ones like object.[23][22]
To enable value types to participate in polymorphic scenarios, C# provides boxing and unboxing mechanisms. Boxing implicitly converts a value type instance to a reference type, such as object, by allocating a new object on the heap and copying the value into it, allowing the value to be treated as an object (e.g., stored in collections). Unboxing explicitly extracts the value back to its original type via a cast, copying the data from the boxed object, but requires exact type matching to avoid exceptions like InvalidCastException. These conversions incur performance overhead due to heap allocation and copying, so they are used judiciously.[26][23]
The type system emphasizes safety through compiler-enforced checks, including restrictions on implicit conversions (e.g., no conversion between bool and integers to prevent logical errors) and support for nullable reference types (introduced in C# 8.0) that generate warnings for potential null dereferences in nullable-enabled contexts. The compiler performs static analysis to detect type mismatches, invalid casts, and unsafe operations at compile time, reducing runtime errors and enhancing code reliability. All types are verifiable under the Common Language Runtime, ensuring that only safe code executes.[23][22]
Control structures and statements
C# provides a variety of control structures and statements to manage the flow of program execution, enabling conditional branching, repetition, and abrupt transfers of control. These mechanisms allow developers to implement logic that responds to conditions, iterates over data, and handles errors gracefully, forming the backbone of procedural programming in the language. All control structures operate on Boolean expressions derived from the type system, such as comparisons between numeric or string types.[27]
Conditionals
The if statement evaluates a Boolean expression and executes a statement or block if the condition is true, optionally followed by an else clause for the false case. It supports chaining via else if for multiple conditions, allowing nested decision-making without deep indentation in simple cases. For example:
csharp
if (temperature < 0)
{
Console.WriteLine("Freezing");
}
else if (temperature < 20)
{
Console.WriteLine("Cold");
}
else
{
Console.WriteLine("Warm");
}
if (temperature < 0)
{
Console.WriteLine("Freezing");
}
else if (temperature < 20)
{
Console.WriteLine("Cold");
}
else
{
Console.WriteLine("Warm");
}
This structure ensures only one path executes, promoting clear, linear flow control.[27]
The switch statement selects among multiple cases based on matching an expression's value or pattern against labels, executing the corresponding block until a jump statement like break terminates it. Unlike earlier languages, C# prevents fall-through by default, requiring explicit jumps to continue to subsequent cases. Introduced in C# 7.0, enhanced pattern matching allows relational patterns (e.g., case < 0:) and guards with when clauses for complex conditions. An example demonstrates value-based switching:
csharp
switch (day)
{
case DayOfWeek.Monday:
case DayOfWeek.Friday:
Console.WriteLine("Start of workweek");
break;
case DayOfWeek.Saturday:
case DayOfWeek.Sunday:
Console.WriteLine("Weekend");
break;
default:
Console.WriteLine("Midweek");
break;
}
switch (day)
{
case DayOfWeek.Monday:
case DayOfWeek.Friday:
Console.WriteLine("Start of workweek");
break;
case DayOfWeek.Saturday:
case DayOfWeek.Sunday:
Console.WriteLine("Weekend");
break;
default:
Console.WriteLine("Midweek");
break;
}
Starting in C# 8.0, the switch expression provides a concise alternative for computing values, returning a result rather than executing side effects, as in result = input switch { 1 => "One", _ => "Other" };. This feature integrates seamlessly with assignment and expression-bodied members, reducing verbosity in functional-style code.[27]
Loops
Iteration statements in C# enable repeated execution of code blocks, with variations controlling the entry and exit conditions. The for loop initializes variables, checks a condition before each iteration, and updates via an iterator expression after the body, ideal for fixed-range counting. Its syntax is for (initializer; [condition](/page/Condition); [iterator](/page/Iterator)) [statement](/page/Statement);, and all parts are optional, allowing flexible use like infinite loops. For instance:
csharp
for (int i = 0; i < 5; i++)
{
Console.WriteLine(i);
}
for (int i = 0; i < 5; i++)
{
Console.WriteLine(i);
}
This outputs numbers 0 through 4, with the condition preventing execution beyond the bound.[28]
The while loop executes its body zero or more times as long as a Boolean condition holds true, evaluating the condition before each iteration to allow early termination. It suits scenarios where the iteration count is unknown upfront, such as processing input until a sentinel value. An example is:
csharp
int count = 0;
while (count < 5)
{
Console.WriteLine(count);
count++;
}
int count = 0;
while (count < 5)
{
Console.WriteLine(count);
count++;
}
In contrast, the do loop guarantees at least one execution by checking the condition after the body, useful for validation prompts or menu systems. Its form is do statement while (condition);, as shown:
csharp
int value = 0;
do
{
value++;
Console.WriteLine(value);
} while (value < 5);
int value = 0;
do
{
value++;
Console.WriteLine(value);
} while (value < 5);
The foreach loop iterates over elements in collections implementing IEnumerable or IEnumerable<T>, deconstructing each item without manual indexing, which simplifies array or list traversal and reduces errors like off-by-one bounds. It throws a NullReferenceException if the collection is null. Example:
csharp
string[] fruits = { "apple", "banana", "cherry" };
foreach (string fruit in fruits)
{
Console.WriteLine(fruit);
}
string[] fruits = { "apple", "banana", "cherry" };
foreach (string fruit in fruits)
{
Console.WriteLine(fruit);
}
This outputs each fruit name sequentially.[28]
Jump Statements
Jump statements alter control flow by transferring execution unconditionally to another point. The break statement exits the nearest enclosing loop (for, while, do, foreach) or switch section, useful for early termination based on a condition. In nested loops, it affects only the innermost one. For example, within a for loop, if (i == 3) break; halts iteration at 3. Similarly, in switch, it prevents unintended fall-through.[29]
The continue statement skips the remainder of the current iteration in the nearest loop and proceeds to the next, evaluating the condition or iterator as appropriate. It does not apply to switch and is ideal for filtering iterations, such as ignoring even numbers: if (i % 2 == 0) continue;. In for loops, it jumps directly to the iterator.[29]
The return statement terminates the enclosing method or property accessor, optionally returning a value matching the declared type (or void for none). It can include an expression for computation, like return radius * height * Math.PI;, and supports ref returns in modern C# to alias variables without copying. This enables early exits from functions based on validation.[29]
The goto statement jumps to a labeled statement within the same method, identified by label:, but is restricted: it cannot enter try blocks or jump over variable initializers, and labels must be unique. Though functional for breaking out of nested loops (e.g., goto done;), its use is discouraged due to reduced readability, with alternatives like break labels preferred. An example in a switch is goto case 1;, but compile-time errors occur for invalid targets.[29]
Exception Handling Basics
Exception handling in C# uses structured blocks to detect and respond to runtime errors, promoting robust code without halting execution. The try statement encloses potentially faulty code, followed optionally by catch for handling specific exception types and finally for cleanup. If no exception occurs, catch and finally still execute as needed; otherwise, control transfers to the matching catch after unwinding the stack. Multiple catch blocks can filter by type hierarchy, evaluated in order. A basic form is:
csharp
try
{
int result = 10 / 0;
}
catch (DivideByZeroException)
{
Console.WriteLine("Division error");
}
finally
{
Console.WriteLine("Cleanup");
}
try
{
int result = 10 / 0;
}
catch (DivideByZeroException)
{
Console.WriteLine("Division error");
}
finally
{
Console.WriteLine("Cleanup");
}
The finally block always runs, even if return or unhandled exceptions occur, ensuring resource disposal like closing files. The throw statement initiates an exception, either creating a new one (e.g., throw new ArgumentException("Invalid");) or re-throwing the current one with just throw;, which preserves the stack trace. This mechanism integrates with the .NET runtime's exception propagation.[30]
Object-oriented programming
Classes and objects
In C#, classes form the cornerstone of object-oriented programming, serving as blueprints for creating objects that encapsulate data and behavior. A class is declared using the class keyword followed by an optional access modifier and the class name, such as public class MyClass { }. This declaration defines a reference type that can include data members like fields and function members like constructors and methods.[31][32]
Fields in a class are variables that store the state of an object, declared with a type and name, for example, private int _value;. They can be instance fields, which belong to each object, or static fields, shared across all instances. Constructors are special methods invoked during object creation to initialize fields; they share the class name and have no return type, such as public MyClass(int initialValue) { _value = initialValue; }. Since C# 12, primary constructors allow concise declaration like public class MyClass(int value) { }, where the parameter acts as a field. In C# 13, partial properties and indexers enable splitting declaration and implementation across partial classes for better modularity in large projects, such as public partial string Name { get; set; } with a separate implementing partial class. Additionally, since C# 14, the field contextual keyword simplifies access to auto-property backing fields in accessors, e.g., set => field = value ?? throw new ArgumentNullException(nameof(value));, reducing boilerplate without explicit field declarations.[31][32][33][34] Destructors, known as finalizers in C#, are optional cleanup methods prefixed with ~, like ~MyClass() { }, executed by the garbage collector before an object is reclaimed, though their use is discouraged in favor of the IDisposable pattern for resource management. In C# 14, partial constructors allow defining and implementing constructors across partial classes, enhancing separation of concerns.[31][32][33]
Objects are instances of classes created using the new keyword, which allocates memory on the managed heap and invokes the constructor, as in MyClass obj = new MyClass(42);. This process creates a concrete entity based on the class definition, allowing multiple objects to exist independently unless explicitly shared.[35][31]
Access modifiers control the visibility of classes and members: [public](/page/Public) allows access from any code; [private](/page/Private) restricts access to within the same class; protected permits access within the class and derived classes; and internal limits access to the same assembly. These modifiers, applied like private int _field;, ensure controlled exposure of class elements.[31][32]
C# distinguishes between instance members, which operate on specific objects (e.g., public int GetValue() { return _value; }), and static members, declared with the static keyword and belonging to the class itself (e.g., public static int Count;), accessed via the class name without instantiation. Static members are useful for shared data or utility functions not tied to object state.[31][32]
Encapsulation in C# is achieved by bundling data and methods within a class while using access modifiers to hide internal implementation details, exposing only necessary interfaces through public members. This principle promotes data integrity and modularity, as private fields can only be modified via controlled methods or properties, preventing direct external manipulation.[31][32]
Inheritance and polymorphism
C# supports single inheritance for classes, allowing a derived class to inherit from exactly one base class, which promotes code reuse by enabling the derived class to access and extend the base class's members.[36] This model is transitive, meaning a class can indirectly inherit from multiple ancestors through a chain of derivations.[36] The base keyword is used within a derived class to access constructors, instance members, or static members of the immediate base class, facilitating initialization and invocation of base functionality.[37]
Method overriding in C# enables derived classes to provide specific implementations of base class methods, enhancing flexibility.[38] To allow overriding, a base class method must be declared with the virtual keyword, indicating it can be replaced in derived classes.[39] A derived class then uses the override keyword to redefine the method, ensuring the new implementation is bound at runtime based on the object's actual type.[40] For instance, consider a base class Shape with a virtual Draw method:
csharp
public class Shape
{
public virtual void Draw()
{
Console.WriteLine("Drawing a shape");
}
}
public class Shape
{
public virtual void Draw()
{
Console.WriteLine("Drawing a shape");
}
}
A derived class Circle can override it as follows:
csharp
public class Circle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a circle");
}
}
public class Circle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a circle");
}
}
If overriding is not desired but a derived class method shares the same name, the new keyword hides the base method, resolving it at compile time based on the reference type.
Polymorphism in C# allows objects of different classes to be treated uniformly through a common base class reference, with behavior determined by the actual object type.[38] Runtime polymorphism, also known as dynamic polymorphism, occurs when virtual or override methods are invoked; the Common Language Runtime (CLR) resolves the call to the most derived implementation at execution time.[38] This enables code like treating a collection of Shape references to invoke the appropriate Draw method for each derived object, such as Circle or Rectangle.[38] Compile-time polymorphism, in contrast, relies on method overloading or the new keyword for name hiding, where the method selected depends on the reference type and parameters at compilation. Casting supports polymorphic usage by converting between base and derived types; implicit casting occurs from derived to base, while explicit casting (e.g., (Shape)circle) is required in the reverse direction to access derived-specific members, potentially throwing an InvalidCastException if incompatible.[38]
Since C# 14, extension members provide an alternative to inheritance for extending existing types, allowing the addition of properties, static members, and operators without modifying the original type or creating derived classes; for example, extending IEnumerable<T> with an IsEmpty property via public static bool IEnumerable<TSource>.IsEmpty(this IEnumerable<TSource> source) => !source.Any();.[41]
To prevent unintended inheritance hierarchies, C# provides the sealed keyword, which can be applied to classes or individual members to prohibit further derivation or overriding. A sealed class cannot serve as a base for other classes, promoting design stability in libraries.
Abstract classes in C# offer a partial implementation for inheritance, declared with the abstract keyword and unable to be instantiated directly; they may include abstract members that derived classes must override.[42] This mechanism enforces a contract for subclasses while allowing shared code, with further details covered in abstract class usage.[42]
Interfaces and abstract classes
In C#, interfaces provide a mechanism for defining contracts that specify behavior without implementation details, enabling multiple types to share common functionalities and supporting multiple inheritance of type and behavior. An interface is declared using the interface keyword and conventionally prefixed with "I", such as IEnumerable<T>. Members of an interface, including methods, properties, events, and indexers, are implicitly public and abstract by default, requiring implementing classes or structs to provide the necessary implementations. Since C# 13, ref struct types can implement interfaces with restrictions to maintain reference safety, allowing high-performance types like spans to fulfill contracts without heap allocation.[43][44]
Classes and structs can implement interfaces either implicitly or explicitly. Implicit implementation involves declaring public members that match the interface's signatures exactly, allowing access through the class instance itself; for example, a Car class implementing IEquatable<Car> would include a public Equals(Car? other) method. Explicit implementation, on the other hand, prefixes the member with the interface name (e.g., bool IEquatable<Car>.Equals(Car? other)), making it accessible only when the instance is treated as the interface type, which is useful for resolving conflicts or hiding implementation details from the public API. Since C# 8.0, interfaces support default implementations for members, allowing classes and structs to inherit these without overriding, thus providing extensibility for existing types; for instance, an interface method like void Paint() => Console.WriteLine("Default paint"); can be defined directly in the interface. In C# 14, partial events in interfaces can have defining and implementing declarations across partial types.[43][45][43][33]
A single class or struct can implement multiple interfaces, facilitating composition of behaviors from diverse sources, though C# prohibits multiple class inheritance to avoid diamond problems. When interfaces share a common base or have overlapping members, explicit implementation resolves ambiguities; for example, a Box class implementing both IEnglishDimensions and IMetricDimensions—each with a Length property—can provide distinct implementations like float IEnglishDimensions.Length => _lengthInches and float IMetricDimensions.Length => _lengthInches * 2.54f, accessed via the respective interface references. This supports polymorphism by allowing a single type to fulfill multiple roles.[43][46]
Abstract classes complement interfaces by offering partial implementations and state, declared with the abstract keyword and serving as non-instantiable base classes for inheritance hierarchies. Unlike interfaces, abstract classes can include concrete members (e.g., implemented methods like public void GetInfo() { ... }), constructors, fields, and protected members, while also declaring abstract members (e.g., public abstract void Move(); or public abstract int MaxSpeed { get; }) that derived classes must override. This allows abstract classes to provide shared functionality—such as common logic in a base Vehicle class—while enforcing specific behaviors in subclasses, differing from interfaces by permitting state and single inheritance only.[42]
Covariance and contravariance enhance flexibility in generic interfaces and delegates, enabling implicit conversions that align with type hierarchies. Covariance, denoted by the out keyword (introduced in .NET Framework 4), applies to output positions like return types, allowing a more derived type to be used where a less derived one is expected; for example, IEnumerable<string> can be assigned to IEnumerable<object>. Contravariance, via the in keyword, applies to input positions like parameters, permitting a less derived type where a more derived one is required, such as assigning Action<object> to Action<string>. These features, supported in delegates since C# 2.0 and extended to generic interfaces, promote reusable and type-safe abstractions without runtime overhead.[47][48]
Interfaces and abstract classes are pivotal in design patterns that emphasize abstraction and interchangeability, such as the Strategy pattern, where an interface defines a family of algorithms (e.g., ISortStrategy with an Execute method), and concrete classes provide implementations that can be injected into a context class for runtime selection. This decouples the client from specific strategies, enhancing maintainability and testing.[49]
Advanced language features
Generics and collections
Generics in C# enable the creation of classes, interfaces, structures, and methods that operate on data types specified as parameters, promoting code reuse, type safety, and performance without the need for explicit type casting. Introduced in C# 2.0 as part of the .NET Framework 2.0, generics addressed limitations of earlier non-generic collections by allowing compile-time type checking, reducing boxing and unboxing overhead for value types.[2][50]
The syntax for generics uses angle brackets to declare type parameters, typically denoted by a single uppercase letter such as T. For a generic class, the declaration follows the form public [class](/page/Class) GenericClass<T> { }, where T serves as a placeholder for any type provided at instantiation, such as GenericClass<int>. Generic methods are declared similarly within classes, with the type parameter preceding the return type: public T Process<T>(T input) { return input; }. Multiple type parameters are supported, as in public [class](/page/Class) [Dictionary](/page/Dictionary)<TKey, TValue> { }, allowing independent type specifications for different roles.[50][51][52]
To ensure type parameters meet specific requirements, C# supports constraints via the where clause. Common constraints include where T : [class](/page/Class) to restrict T to reference types, where T : struct for value types, where T : new() to require a parameterless constructor, and where T : SomeInterface or where T : BaseClass to enforce inheritance or implementation. These constraints enable access to members like constructors or methods on T within the generic code. For example:
csharp
[public](/page/Public) class Repository<T> where T : [class](/page/Class), new()
{
[public](/page/Public) T Create() => new T();
}
[public](/page/Public) class Repository<T> where T : [class](/page/Class), new()
{
[public](/page/Public) T Create() => new T();
}
This ensures T is instantiable and a reference type. C# 13 introduced the allows ref struct anti-constraint, enabling generic types to accept ref struct types like Span<T>.[53][54]
C# provides a rich set of built-in generic collections in the System.Collections.Generic namespace, designed for type-safe storage and manipulation. List<T> implements a dynamic array that grows as needed, supporting indexed access, addition, removal, and sorting operations on elements of type T. Dictionary<TKey, TValue> stores key-value pairs with fast lookups via hashing, where TKey must be unique and hashable. IEnumerable<T> serves as the foundational interface for read-only iteration over sequences of T, enabling foreach loops and integration with other collection types. These collections replaced legacy non-generic alternatives, offering better performance and avoiding runtime errors from type mismatches.[55][56][57][58]
Covariance and contravariance, added in C# 4.0, enhance generic type compatibility by allowing implicit conversions between related types under certain conditions. Covariance permits assigning a generic type with a more derived type argument to one with a less derived argument, marked by the out keyword on interfaces or delegates (e.g., IEnumerable<out T>), which is useful for output-only scenarios like returning sequences. Contravariance does the opposite for input parameters, using the in keyword (e.g., IComparer<in T>), allowing a method expecting a base type to accept a derived type. For instance, IEnumerable<string> can be assigned to IEnumerable<object> due to covariance, while Action<object> can be assigned to Action<string> due to contravariance. These features apply only to reference types and interfaces declared as variant.[59][47][60][61]
Subsequent enhancements have built on these foundations; for example, C# 12 introduced collection expressions, allowing concise initialization of generic collections like List<int> numbers = [1, 2, 3];, which spreads or targets existing generics for improved readability and efficiency. C# 13 added params collections, enabling params parameters to accept collections like Span<T> or IEnumerable<T> directly. C# 14 introduced implicit conversions between arrays and Span<T>/ReadOnlySpan<T>, along with extension members for IEnumerable<T> such as an IsEmpty property and static Combine method, and support for unbound generic types in nameof.[62][54][3]
Language Integrated Query (LINQ)
Language Integrated Query (LINQ) is a set of technologies integrated into the C# language that provides a unified, declarative approach to querying data from diverse sources, such as in-memory collections, databases, XML documents, and web services.[63] Introduced with C# 3.0 in November 2007 as part of the .NET Framework 3.5, LINQ enables developers to write type-safe queries using familiar SQL-like syntax directly in code, with full compile-time checking and IntelliSense support.[63] This integration treats queries as first-class language constructs, bridging the gap between object-oriented programming and data access patterns.[64]
LINQ supports multiple providers tailored to different data sources, allowing consistent querying across environments. LINQ to Objects operates on in-memory collections that implement IEnumerable, enabling queries on arrays, lists, and other generic collections.[63] LINQ to XML facilitates manipulation and querying of XML data structures, treating them as object hierarchies.[63] For relational databases, LINQ to SQL translates queries into SQL statements for direct database interaction, while LINQ to Entities, used with Entity Framework Core, handles more complex object-relational mapping scenarios.[63]
The query syntax in LINQ resembles SQL, using keywords to define operations in a readable, declarative manner. A typical query begins with the from clause to specify the data source and range variable, followed by where for filtering, select for projection, join for combining sources, and group by for aggregation.[65] For example, developers can filter a collection of objects by a condition, project specific properties, and group results by a key, all within a single expression.[63]
Equivalently, LINQ offers method syntax through extension methods in the System.Linq namespace, which use lambda expressions for more programmatic control. Common methods include Where() for filtering, Select() for transforming elements, Join() for correlating sequences, and GroupBy() for partitioning data.[63] This syntax compiles to the same intermediate representation as query syntax, allowing seamless mixing of both styles in the same codebase.[63]
LINQ queries employ deferred execution, meaning the query is not evaluated until its results are enumerated, such as in a foreach loop or via methods like ToList() or ToArray().[63] This lazy evaluation improves performance by avoiding unnecessary computations. The core interfaces are IEnumerable for local, in-memory queries, where operations compile to delegate-based code, and IQueryable for remote or provider-specific queries, which build expression trees that providers can translate to native query languages like SQL.[64]
C# 14 enhances LINQ through extension members, allowing the addition of new instance properties like IsEmpty and static operators such as + for sequence concatenation on IEnumerable<T>.[3]
Asynchronous programming
C#'s asynchronous programming model, known as the Task-based Asynchronous Pattern (TAP), enables developers to write non-blocking code for I/O-bound and CPU-bound operations, improving application responsiveness without traditional threading complexities.[66] Introduced in C# 5.0 with the .NET Framework 4.5, TAP leverages the async and await keywords to simplify asynchronous code, allowing it to read like synchronous code while executing concurrently.[67] This model replaced older patterns like the Asynchronous Programming Model (APM) and Event-based Asynchronous Pattern (EAP), providing a unified approach for representing and composing asynchronous operations.[66]
At the core of TAP are the Task and Task<T> types from the System.Threading.Tasks namespace. A Task represents an ongoing asynchronous operation that may complete successfully, fault, or be canceled, without returning a value.[66] In contrast, Task<T> extends this to include a result of type T upon completion, such as Task<string> for an HTTP response body.[68] These types support chaining, cancellation, and error propagation, forming the foundation for scalable concurrent programming in C#. For example, creating a task might involve Task.Run(() => { /* work */ }) to schedule CPU-intensive code on a thread pool thread.[69]
The async and await keywords facilitate readable asynchronous methods. The async modifier declares a method as asynchronous, permitting the use of await and typically returning a Task or Task<T>.[70] The await operator pauses the method's execution until the awaited task completes, yielding control back to the caller without blocking the thread, then resumes with the task's result.[68] Consider this example for fetching data concurrently:
csharp
public async Task<string> FetchDataAsync()
{
using var client = new HttpClient();
return await client.GetStringAsync("https://example.com");
}
public async Task<string> FetchDataAsync()
{
using var client = new HttpClient();
return await client.GetStringAsync("https://example.com");
}
This avoids deadlocks common in synchronous calls and scales better for UI or server applications.[67]
For finer control, ConfigureAwait allows specifying whether the continuation after await should capture the current synchronization context, such as the UI thread in WPF or the request context in ASP.NET.[71] Using ConfigureAwait(false) in library code prevents unnecessary context switches and potential deadlocks, as the default true preserves context for thread-affinity needs.[71] Cancellation is handled via CancellationToken, a struct passed to async methods to signal cooperative cancellation; tasks can register callbacks or throw OperationCanceledException when checked.[72] A CancellationTokenSource creates and manages tokens, enabling timed or manual cancellation, as in:
csharp
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
await SomeAsyncOperationAsync(cts.Token);
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
await SomeAsyncOperationAsync(cts.Token);
This ensures resources are freed promptly in long-running operations.[73]
Common patterns include Task.WhenAll for awaiting multiple tasks in parallel, which completes when all provided tasks finish or any faults, returning results via Task<T[]> for typed collections.[74] For instance, processing multiple URLs concurrently uses await Task.WhenAll(urls.Select(FetchDataAsync)) to aggregate responses efficiently.[67] Task.Run schedules synchronous code asynchronously on the thread pool, ideal for CPU-bound work without I/O, distinguishing it from await on inherently async APIs like file reads.[69]
Later versions introduced optimizations like ValueTask and ValueTask<T> in C# 7.0 and .NET Core 2.0, reducing heap allocations for async methods that often complete synchronously by allowing stack-based results.[75] These are awaitable like Task but should not be awaited multiple times, suiting hot-path scenarios such as cached computations.[75] Further enhancements in .NET 5 added pooling for ValueTask to minimize garbage collection in high-throughput async code. C# 13 extended async methods to support ref locals and ref struct types (without crossing await boundaries) and unsafe contexts.[76][54]
Error handling in async code captures exceptions in the task rather than propagating immediately. When an awaited task faults, the exception is rethrown at the await site, allowing standard try-catch blocks to handle it.[67] For multiple tasks, Task.WhenAll aggregates faults into an AggregateException accessible via Task.Exception, enabling selective unwrapping.[67] Unobserved exceptions in non-awaited tasks can crash the process in .NET Framework but are handled more gracefully in .NET Core, emphasizing explicit awaiting for robustness.[67]
Functional programming elements
C# incorporates several functional programming paradigms, enabling developers to write more declarative and composable code. These features, evolving since C# 3.0, include support for higher-order functions, immutability, and pattern-based logic, which facilitate treating functions as first-class citizens and emphasizing pure, side-effect-free operations.[2]
Delegates form the foundation for functional elements in C#, serving as type-safe function pointers that reference methods with specific signatures. Introduced in C# 1.0, delegates allow methods to be passed as parameters, enabling callback patterns and event handling. Events, built atop delegates, provide a publisher-subscriber model where classes can notify subscribers of state changes without tight coupling; for instance, the event keyword restricts external access to multicast delegates, ensuring only the declaring class can invoke them.[77][78]
Lambda expressions, added in C# 3.0, extend delegates by allowing concise anonymous functions using the => operator, which separates parameters from the body. Expression lambdas (e.g., x => x * 2) produce read-only expression trees, while statement lambdas (e.g., x => { return x * 2; }) support multi-statement bodies. These simplify code by replacing verbose delegate declarations and are integral to functional composition. Anonymous methods, predating lambdas in C# 2.0 via the delegate keyword, offer similar functionality but with less syntactic sugar. C# 14 allows lambda parameters to use modifiers like ref or out without explicit types, enhancing flexibility in functional code.[79][80][3]
Higher-order functions in C# leverage generic delegates like Func<T, TResult>, which represents a function taking input of type T and returning TResult, and Action<T>, which denotes a void-returning procedure. Predicates, using Func<T, bool>, test conditions functionally. These types, available since .NET Framework 2.0, allow functions to accept or return other functions, promoting reusability; for example, List<T>.Find uses a Predicate<T> to filter elements declaratively.[81][82]
Immutability is supported through readonly members, which prevent field modification after initialization, and records introduced in C# 9.0. Records are reference types (or value types via record struct) with built-in value equality, immutability via init-only properties, and concise syntax like positional records (e.g., record Point(int X, int Y);). The with expression enables non-destructive updates, creating modified copies without altering originals, aligning with functional principles of persistence.[83][84]
Pattern matching, enhanced starting in C# 7.0, allows declarative deconstruction and testing of data structures using is and switch expressions. For example, if (obj is Point { X: > 0 } p) binds and tests properties simultaneously, reducing boilerplate in conditional logic. Switch expressions (e.g., shape switch { [Circle](/page/Circle) c => Math.PI * c.Radius * c.Radius, _ => 0 }) provide exhaustive, functional-style dispatching based on type or value patterns.[85][86]
Expression trees represent lambda expressions as inspectable data structures in the System.Linq.Expressions namespace, enabling dynamic code generation and analysis. Compiled from lambdas at runtime, they form trees of nodes like BinaryExpression for operations (e.g., x + y), allowing manipulation for scenarios like query translation. Introduced with .NET 3.5, they underpin functional query composition without immediate execution. C# 14 introduces extension members, enabling the addition of properties, indexers, and static members to existing types like IEnumerable<T>, further supporting functional extensions.[87][3]
These elements, originating prominently in C# 3.0 with lambda expressions and extension methods, have evolved through versions like C# 9.0's records and C# 11's refined patterns, fostering a blend of imperative and functional styles. Language Integrated Query (LINQ) exemplifies their application in declarative data manipulation.[2]
Libraries and ecosystem
Standard libraries
The standard libraries in C# are part of the .NET Base Class Library, providing a rich set of reusable types and members for common programming tasks.[88] These libraries are organized into namespaces that offer foundational functionality, enabling developers to perform operations like input/output, data manipulation, and concurrency without external dependencies.
The System namespace serves as the root for fundamental types and utilities in C#.[89] It includes the Console class, which manages standard input and output streams for console applications, allowing methods like WriteLine for displaying text and ReadLine for user input.[90] The Math class provides static methods and constants for common mathematical operations, such as trigonometric functions (Sin, Cos), logarithms, and rounding (Round, Floor).[91] Additionally, DateTime represents instants in time, supporting creation, formatting, and arithmetic like adding intervals or comparing dates.[92] The Random class generates pseudo-random numbers for tasks requiring statistical randomness, using a seed-based algorithm to produce sequences of integers or doubles.[93]
The System.IO namespace handles input and output operations, particularly for files and streams.[94] The File class offers static methods for file management, including creating, reading, writing, copying, moving, and deleting files, often returning streams for further processing.[95] Stream is an abstract base class that provides a generic interface for reading and writing sequences of bytes, serving as the foundation for derived classes like FileStream or MemoryStream.[96] The Path class includes utilities for manipulating file paths in a platform-independent manner, such as combining directory and file names (Combine), extracting extensions (GetExtension), or checking validity (IsPathRooted).[97]
System.Collections contains interfaces and classes for non-generic collections, which are legacy types still available for backward compatibility but generally superseded by generic alternatives in modern code.[98] Key examples include ArrayList, a dynamic array that grows as elements are added; Hashtable, which stores key-value pairs using hash codes for fast lookups; Queue for first-in-first-out operations; and Stack for last-in-first-out behavior.[99][100] Interfaces like ICollection define basic enumeration and sizing, IDictionary handles key-based access, and IList supports indexed retrieval.[101]
The System.Linq namespace provides extension methods essential for Language Integrated Query (LINQ), enabling declarative querying of data sources.[102] The Enumerable class includes static methods like Where for filtering, Select for projection, and OrderBy for sorting, which extend IEnumerable<T> to support in-memory queries.[103] Queryable offers similar methods for IQueryable<T>, facilitating deferred execution in scenarios like database queries.[104] These methods integrate seamlessly with C# syntax, using lambda expressions for concise data manipulation.
System.Threading supplies basic primitives for multithreaded programming, focusing on synchronization and thread management.[105] The Monitor class enables mutual exclusion with methods like Enter and Exit to protect critical sections from concurrent access.[106] Interlocked performs atomic operations on variables, such as incrementing or exchanging values without race conditions. Other primitives include Mutex for inter-process synchronization, AutoResetEvent for signaling single threads, and ThreadPool for queuing work items to a pool of background threads.[107][108]
Common Language Infrastructure (CLI)
The Common Language Infrastructure (CLI) is an open specification that defines a runtime environment for executing managed code written in multiple high-level programming languages, enabling cross-language interoperability and platform portability. Standardized as ECMA-335 by Ecma International and ISO/IEC 23271, it encompasses the core components necessary for compilation, execution, and management of applications on various operating systems.[109] The CLI serves as the foundation for implementations like the .NET runtime, where source code is transformed into an intermediate form for just-in-time execution.[110]
The Common Type System (CTS), a core partition of the CLI, establishes a unified framework for defining, declaring, and managing types across languages, ensuring consistent behavior and seamless interaction between components developed in different languages. It supports an object-oriented model with categories including reference types (such as classes and interfaces), value types (like structures and enumerations), and primitive types (e.g., Boolean, Int32), all deriving from the base type System.Object to promote inheritance and polymorphism.[111] This type unification allows, for instance, a C# class to be referenced and extended in a Visual Basic .NET application without compatibility issues, as the CTS enforces rules for type safety, visibility (public or private), and layout.[111]
Metadata in the CLI provides a language-agnostic, binary description of program elements, including types, members, references, and attributes, embedded directly within assemblies to create self-describing components. Assemblies, manifested as portable executable (PE) files in .exe (executable) or .dll (library) formats, bundle this metadata with Common Intermediate Language (IL) code and a manifest detailing version, culture, and security identity.[112] The manifest enables the runtime to resolve dependencies, enforce versioning, and apply security policies, while the metadata tables allow dynamic loading and verification without requiring external type libraries or registration.[113]
Garbage collection in the CLI automates memory management through a mark-and-sweep collector operating on a managed heap, a dedicated contiguous address space for allocating and deallocating objects to prevent leaks and fragmentation. The heap is segmented into a small object heap for objects under 85 KB and a large object heap for bigger allocations like arrays, with the collector using generational collection to prioritize efficiency: Generation 0 holds short-lived objects (e.g., temporary variables) for frequent, low-cost collections; survivors promote to Generation 1, an intermediary buffer; and long-lived objects (e.g., static fields) reside in Generation 2, triggering full heap collections only when necessary.[114] This approach leverages the observation that most objects die young, minimizing pause times and optimizing throughput in server and workstation modes.[114]
The CLI's execution model relies on Just-In-Time (JIT) compilation, where language compilers first translate source code into verifiable, platform-independent Intermediate Language (IL) instructions, stored in assemblies with metadata tokens for reference resolution. During runtime, the JIT compiler converts IL to native machine code optimized for the host CPU, performing optimizations like inlining and loop unrolling while enforcing type safety through verification to catch errors like invalid casts.[115] This two-stage process balances development productivity with runtime performance, allowing the same assembly to run across architectures via adaptive compilation.[115]
The CLI's security model historically included Code Access Security (CAS), a declarative and imperative framework that granted permissions (e.g., file I/O or network access) based on code evidence such as digital signatures or zones, using a stack-walk mechanism to ensure callers had requisite rights and prevent elevation of privilege.[116] However, CAS was deprecated as unsupported legacy technology in .NET Framework post-4.0 due to complexity and security gaps, and in .NET Core and .NET 5+, CAS APIs are obsolete—issuing compile warnings and either ignoring attributes or throwing PlatformNotSupportedException—relying instead on transparent code execution, OS-level isolation, and modern cryptography for protection.[117]
Interoperability
C# provides robust interoperability mechanisms that allow seamless integration with unmanaged code, legacy systems, and other programming languages within the .NET ecosystem. These features enable developers to leverage existing native libraries, COM components, and cross-platform APIs while maintaining the benefits of managed code execution. The Common Language Infrastructure (CLI) serves as the foundational enabler for much of this integration by standardizing metadata and execution models across languages.[118]
Platform Invoke (P/Invoke) facilitates calling functions, structs, and callbacks from native DLLs in unmanaged code, such as those written in C or C++. Developers declare external methods using the [LibraryImport] attribute to specify the DLL path and marshaling details, ensuring type compatibility between managed and unmanaged environments. For instance, invoking the Windows MessageBoxW function from user32.dll requires defining a partial method signature in C# that matches the native prototype, with automatic marshaling for strings and pointers via IntPtr. This mechanism supports cross-platform scenarios, like calling getpid on Linux from libc.so.6, and includes error handling through Marshal.GetLastPInvokeError. P/Invoke is essential for integrating legacy native libraries without rewriting them.[119]
COM interop bridges C# with Component Object Model (COM) components, allowing managed code to consume or expose COM objects. The [ComImport] attribute marks a C# interface or class as an import from a COM type library, enabling direct access to COM functionality without generating interop assemblies manually. When a .NET client invokes a COM object, the runtime creates a Runtime Callable Wrapper (RCW) that abstracts differences in memory management, reference counting, and interface querying between the two models. The RCW handles marshaling, lifetime management, and exception translation, ensuring transparent calls from C# to COM. Conversely, COM Callable Wrappers (CCWs) allow COM clients to interact with .NET objects. This setup supports legacy Windows applications, such as Office automation, by qualifying types for interoperation.[120][121][122]
Within the .NET ecosystem, C# exhibits strong cross-language interoperability, permitting code written in C# to directly call and inherit from components in other .NET languages like VB.NET or F#. This is achieved by compiling modules from different languages into a shared assembly, where metadata ensures type compatibility and method resolution. For example, a C# application can reference a VB.NET class library containing string utilities and invoke its methods without language-specific barriers, as all compile to Common Intermediate Language (IL). Similarly, F# functional constructs can be consumed in C# projects, fostering mixed-language development in unified solutions. This language independence promotes code reuse across the .NET platform.[118]
.NET Standard enhances multi-platform compatibility by defining a common set of APIs that C# libraries can target, ensuring they work across diverse .NET implementations like .NET Framework, .NET Core, and Xamarin. Libraries built against .NET Standard 2.0, which includes over 32,000 APIs, can be consumed by applications on Windows, macOS, Linux, and mobile platforms without modification. This specification acts as a contract for interoperability, allowing a single C# library to support both legacy .NET Framework desktop apps and modern cross-platform .NET 8+ runtimes. Targeting .NET Standard is recommended for shared libraries to maximize portability.[123]
Blazor, leveraging WebAssembly, enables C# code to run in web browsers and interoperate directly with JavaScript and browser APIs. Through JavaScript interop, C# methods can invoke JS functions via the IJSRuntime interface, passing parameters asynchronously and handling results with JSON serialization. For example, a Blazor component might call a JS function to access the browser's geolocation API, while JS can invoke exported .NET methods using [JSExport]. This bidirectional integration, optimized for performance with unmarshaled calls, allows full-stack C# development for web UIs without relying solely on JavaScript. WebAssembly execution ensures C# runs natively in the browser sandbox.[124]
Microsoft implementations
Microsoft's implementations of C# are centered around the .NET platform, which provides the runtime, libraries, and compiler infrastructure necessary for developing and executing C# applications. The primary compiler is Roslyn, an open-source .NET Compiler Platform that handles parsing, semantic analysis, and code generation for C# and Visual Basic. Roslyn was first released as open-source under the Apache 2.0 license on April 3, 2014, enabling developers to build custom tools, analyzers, and extensions directly on top of the compiler APIs. It powers the C# compilation in Visual Studio and the .NET CLI, supporting incremental compilation for improved performance in large projects.
The foundational runtime for early C# implementations was the .NET Framework, a Windows-specific platform that evolved from version 1.0, released in February 2002, to version 4.8 in April 2019. .NET Framework 1.0 introduced the Common Language Runtime (CLR) version 1.0, enabling managed code execution with garbage collection and just-in-time compilation. Key subsequent releases included .NET Framework 2.0 (November 2005, CLR 2.0 with generics support), 3.5 (November 2007, adding LINQ), 4.0 (April 2010, CLR 4.0 with dynamic typing), and up to 4.8, which incorporated security enhancements and accessibility improvements while maintaining backward compatibility. The .NET Framework remains supported for legacy Windows applications but is no longer receiving major feature updates.
To address cross-platform needs, Microsoft launched .NET Core in June 2016 as a modular, open-source runtime, starting with version 1.0 and progressing through 3.1 (December 2019, the last long-term support release before unification). .NET Core emphasized performance optimizations, such as the RyuJIT compiler, and supported Linux, macOS, and Windows. In November 2020, .NET 5 marked the unification of .NET Framework and .NET Core into a single cross-platform platform, eliminating divergence in APIs and tools; this continued with annual releases like .NET 6 (November 2021, long-term support with improved container support), .NET 7 (November 2022), and .NET 8 (November 2023). The unified .NET platform now uses a single runtime codebase, with .NET Standard libraries ensuring compatibility across implementations.
For compatibility with existing codebases, modern .NET includes layers derived from the Mono project, an open-source .NET implementation originally developed by Xamarin. Following Microsoft's acquisition of Xamarin in 2016, Mono's runtime was integrated into .NET for non-Windows scenarios, particularly to handle platform-specific behaviors in mobile and embedded environments. This ensures that .NET applications can run legacy Mono-compatible code without modification, leveraging Mono's class libraries for features like ahead-of-time compilation on iOS.
Microsoft's mobile and desktop development has been advanced through Xamarin, a C#-based framework for cross-platform apps, and its successor .NET Multi-platform App UI (MAUI). Xamarin, released in 2011 and fully integrated into .NET by 2016, allowed shared C# code for iOS, Android, and Windows apps using native UI controls. Support for Xamarin ended on May 1, 2024, with migration paths to .NET MAUI, introduced in .NET 6 (May 2022), which unifies Xamarin.Forms, Xamarin.Essentials, and WPF/WinUI into a single framework for building native apps across Android, iOS, macOS, Windows, and Tizen from a shared C# codebase. .NET MAUI leverages the unified .NET runtime for deployment, with tools like the .NET Upgrade Assistant facilitating transitions from Xamarin projects.
As of November 2025, the latest Microsoft implementation is .NET 10, released on November 11, 2025, which natively supports C# 14 features such as improved pattern matching, extension members, and file-based application structures. .NET 10 builds on the unified platform with runtime improvements, including performance enhancements for cloud-native scenarios, while maintaining full compatibility with prior C# versions.[125][3]
Alternative implementations
Alternative implementations of C# extend the language's reach beyond Microsoft's proprietary ecosystem, emphasizing open-source, cross-platform, and specialized runtimes and compilers that adhere to ECMA standards for the Common Language Infrastructure (CLI). These efforts enable C# development on non-Windows platforms, in game engines, and through community-driven tools, often providing full or partial compatibility with .NET libraries.[126][127]
The Mono project, initiated in 2001 by Miguel de Icaza, represents a pioneering open-source implementation of the .NET Framework, designed specifically for Linux, macOS, and other Unix-like systems. It includes a complete runtime environment with just-in-time (JIT) and ahead-of-time (AOT) compilers, a garbage collector, and class libraries that mirror Microsoft's offerings, such as ASP.NET and ADO.NET, while adding platform-specific extensions like Gtk+ for graphical interfaces. Mono supports C# versions from 1.0 up to 6.0 and has been instrumental in cross-platform applications, including mobile development via Xamarin, which was later integrated into Microsoft's ecosystem. Originally sponsored by Novell and then Xamarin, Mono transitioned under the .NET Foundation in 2016, fostering a vibrant community that ensures binary compatibility with Microsoft APIs on diverse architectures like x86 and ARM. Its modular design allows developers to build server, desktop, and embedded applications without relying on Windows, significantly broadening C#'s adoption in open-source environments.[128][126][129]
CoreCLR serves as the foundational open-source runtime for .NET Core, released by Microsoft in 2015 to support modular, cross-platform C# applications. Comprising approximately 2.6 million lines of code in C# and C++, it includes the RyuJIT compiler, a high-performance garbage collector, and native interop layers, enabling execution on Windows, Linux, and macOS. Unlike full .NET Framework implementations, CoreCLR focuses on lightweight, container-friendly scenarios such as cloud services and microservices, with initial Linux support following its open-sourcing on GitHub under the MIT license. This runtime powers .NET Core's ability to run C# code in serverless environments and has been extended by the community for embedded systems, emphasizing portability and performance optimizations like tiered compilation.[127][130]
Historically, the DotGNU Portable.NET project, launched in 2001 as part of the GNU initiative, aimed to create a fully free software alternative to Microsoft's .NET for CLI-compliant applications. It featured the cscc compiler, a modular tool written in C that supported C# (ECMA-334 compliant) alongside C, with partial handling for Java and VB.NET through the Tree Compiler Compiler (treecc) framework. The runtime, ilrun, interpreted Common Intermediate Language (CIL) bytecode using a Converted Virtual Machine (CVM) and supported native code generation on platforms like x86, ARM, PowerPC, and Sparc across GNU/Linux, Windows, macOS, and BSD variants. Portable.NET also implemented System.Windows.Forms in pure C# for cross-platform GUIs, but development ceased around 2008, leaving it as a foundational yet decommissioned effort that influenced later open-source .NET tools.[131][132]
In specialized domains like game development, Unity's IL2CPP provides an alternative scripting backend for C# by converting Microsoft Intermediate Language (MSIL) from C# scripts into C++ code, which is then compiled into native binaries for deployment. Introduced to replace Mono's JIT-based approach, IL2CPP enables ahead-of-time (AOT) compilation, enhancing performance and compatibility on platforms prohibiting runtime code generation, such as iOS and consoles. The process involves Roslyn compiling C# to DLLs, followed by managed code stripping for size reduction and IL2CPP's translation to C++, resulting in faster execution and smaller binaries compared to interpreted alternatives, though at the cost of longer build times. Widely adopted in Unity's game engine, IL2CPP supports debugging akin to Mono and targets diverse ecosystems, including Universal Windows Platform and Android, making C# viable for high-performance interactive applications.[133]
Community-driven compilers offer further alternatives to Microsoft's csc (part of Roslyn), with Mono's mcs standing out as an early open-source C# compiler written in C# itself. Developed alongside the Mono runtime, mcs provided full support for C# up to version 6.0, including features like generics and LINQ, and was optimized for cross-platform builds on Linux and macOS. It served as the default compiler in Mono distributions until version 5.0 in 2017, when it was largely superseded by Roslyn for ongoing maintenance, though legacy support persists in certain embedded scenarios. Similarly, DotGNU's cscc emphasized modularity and multi-language integration, compiling C# to CIL with strong ECMA compliance, but its historical status limits modern use. These compilers highlight community efforts to decouple C# compilation from proprietary tools, enabling independent experimentation and portability.[134][131][126]
Visual Studio serves as the primary integrated development environment (IDE) for C# development, offering editions tailored to different user needs. The Community edition provides free access for individual developers, students, and open-source projects, including core features like code editing and debugging. Professional and Enterprise editions add advanced capabilities such as architecture tools, live dependency validation, and enhanced testing for larger teams and enterprises.[135][136]
Key features in Visual Studio include IntelliSense, which offers context-aware code completion, parameter information, and quick info tooltips to accelerate coding in C#. Debugging tools enable setting breakpoints, stepping through code, inspecting variables via DataTips, and evaluating expressions in the Immediate Window during execution. These features integrate seamlessly with the Roslyn compiler for real-time error detection and refactoring.[137][138][139]
Visual Studio Code (VS Code), a lightweight cross-platform editor, supports C# through the official C# Dev Kit extension, which leverages OmniSharp as the language server for features like syntax highlighting, code completion, and error squiggles. This setup allows developers to build, run, and debug C# applications on Windows, macOS, and Linux without the full Visual Studio installation.[140][141]
For command-line workflows, the C# compiler (csc.exe) compiles source files into assemblies, supporting options for optimization, debugging symbols, and target outputs like executables or libraries. The .NET CLI (dotnet) complements this by providing commands such as dotnet build, dotnet run, and dotnet test for managing projects, dependencies, and execution across platforms.[142]
JetBrains Rider offers a cross-platform alternative IDE with intelligent code analysis, including over 2,200 inspections and 1,000 quick-fixes for C#, alongside navigation tools, decompilation, and unit testing support. It emphasizes productivity through features like whole-line code completions and seamless integration with .NET ecosystems.[143]
C# development tools integrate well with testing frameworks like xUnit and NUnit. In Visual Studio, the Test Explorer discovers, runs, and debugs tests from these frameworks via NuGet packages, providing code coverage analysis and live unit test results. Similar integration exists in VS Code and Rider, enabling automated test execution within the IDE.[144][145][146]
Standardization and adoption
ECMA and ISO standards
C# was initially developed by Microsoft as part of the .NET Framework, with standardization efforts beginning in September 2000 through Ecma International's Technical Committee 39 (TC39, later renamed TC49) Task Group 2 (TG2).[1][11] The first edition of the C# language specification, ECMA-334, was adopted by the Ecma General Assembly in December 2002, defining the syntax, semantics, and constraints for C# programs.[147][11]
This ECMA standard was fast-tracked for adoption by the International Organization for Standardization (ISO) and the International Electrotechnical Commission (IEC), resulting in ISO/IEC 23270:2003, which is technically equivalent to ECMA-334 and serves as the international standard for the C# programming language. Subsequent editions, up to the third edition (ISO/IEC 23270:2018), maintained equivalence with corresponding ECMA revisions up to that point.[11][148] However, while ECMA-334 has continued to evolve, the ISO standard has not been updated since 2018; Microsoft and Ecma International continue to submit revisions for fast-track ISO adoption, but as of November 2025, the ISO remains aligned with earlier C# versions.[149]
The C# specifications are provided as open standards under royalty-free licensing terms, allowing any party to freely implement the language without intellectual property encumbrances, as affirmed by Microsoft's Community Promise and Ecma's policies.[150][149] The latest edition, ECMA-334 7th edition (December 2023), supersedes the 2022 version and includes updates for features introduced up to C# 12, such as primary constructors.[151][147]
Ongoing standardization ensures compliance with newer language versions; for instance, features from C# 13 (e.g., params collections) and C# 14 (e.g., enhanced pattern matching) are incorporated into future editions through TC49's revision process to reflect the evolving specification.[149][62][54][3]
Usage and popularity
C# is predominantly utilized in the development of Windows and .NET-based desktop applications, web applications via ASP.NET, game development through engines like Unity, and mobile applications using frameworks such as Xamarin and .NET MAUI.[152][153][154][155]
In terms of popularity, C# consistently ranks among the top programming languages in major indices. As of November 2025, it holds the fifth position in the TIOBE Index with a 7.65% rating, reflecting its strong community interest and search volume. The 2025 Stack Overflow Developer Survey reports C# as the eighth most commonly used language, with 27.8% of respondents employing it, particularly in .NET development and gaming contexts.[156][157]
Enterprise adoption of C# remains robust within the Microsoft ecosystem, where it dominates for building scalable applications integrated with Azure cloud services. In 2025, the growing demand for C# developers in India and globally stems from widespread Azure and .NET adoption, enabling efficient cloud-native solutions for businesses.[158][153][159]
The language has seen significant growth in cross-platform capabilities since .NET 5, allowing developers to build and deploy applications across Windows, Linux, and macOS environments. This evolution, building on .NET Core's open-source foundation, has expanded C#'s reach beyond Windows-centric development, supporting unified platforms for web, desktop, and mobile in 2025.[160][153][161]
Emerging trends in 2025 highlight C#'s increasing integration with AI and machine learning, facilitated by tools like ML.NET and Azure AI services, which enable .NET developers to incorporate intelligent features such as predictive analytics and automation directly into applications. Frameworks like ML.NET allow seamless AI model training and deployment within C# ecosystems, positioning the language as a viable option for AI-driven enterprise solutions.[162][163][164]
A common criticism of C# has been its historical platform dependency on Windows, which limited portability in earlier .NET Framework versions. However, .NET Core and subsequent releases have mitigated this by introducing cross-platform support from the ground up, enabling deployment on non-Windows operating systems and reducing vendor lock-in concerns.[165][160]
Examples
Hello World program
The "Hello World" program serves as the canonical introductory example in C#, demonstrating the language's fundamental syntax and execution flow. It outputs the text "Hello World" to the console, illustrating how a minimal C# application is structured and run. This example highlights essential elements such as namespaces, classes, and the entry point method, providing a foundation for understanding more complex programs.
A basic "Hello World" program in C# is written as follows:
csharp
using [System](/page/System);
[class](/page/Class) [Program](/page/Program)
{
static void [Main](/page/Method)()
{
[Console](/page/System).WriteLine("[Hello World](/page/Hello_World)");
}
}
using [System](/page/System);
[class](/page/Class) [Program](/page/Program)
{
static void [Main](/page/Method)()
{
[Console](/page/System).WriteLine("[Hello World](/page/Hello_World)");
}
}
This code begins with a using directive that imports the System namespace, granting access to core classes like Console without fully qualifying their names. The program defines a class named Program, which encapsulates the Main method—this static method serves as the application's entry point, invoked automatically when the program starts. Inside Main, the Console.WriteLine method from the System namespace prints the string "Hello World" followed by a newline to the standard output stream.
To compile and execute this program, developers typically use the C# compiler (csc.exe) provided by the .NET SDK. First, save the code in a file with a .cs extension, such as HelloWorld.cs. From the command line, run csc HelloWorld.cs to generate an executable assembly (e.g., HelloWorld.exe on Windows). Then, execute it with HelloWorld.exe, which will display "Hello World" in the console. Alternatively, integrated development environments (IDEs) like Visual Studio automate these steps, handling compilation, linking to the .NET runtime, and launching the application. The program relies on the Common Language Runtime (CLR) for execution, where the just-in-time (JIT) compiler translates the intermediate language (IL) code into machine instructions.
Starting with C# 9, introduced in .NET 5, a simplified variant using top-level statements eliminates the need for explicit class and method declarations, making the code more concise for simple programs. This version appears as:
csharp
Console.WriteLine("Hello World");
Console.WriteLine("Hello World");
Implicitly, the compiler wraps this in a Main method within a generated class, preserving compatibility with the runtime while reducing boilerplate. This feature streamlines scripting and educational examples but is not intended for larger applications.
Generic example
Generics in C# enable the creation of reusable classes that maintain type safety at compile time, allowing developers to define classes like Stack<T> that work with any specified type T without sacrificing performance or introducing runtime errors. The following example illustrates a simple implementation of a generic Stack<T> class, which represents a last-in, first-out (LIFO) data structure. This class uses an array to store elements and includes basic Push and Pop methods, with a type parameter constraint where T : IComparable<T> to ensure elements can be compared, enabling potential extensions like sorting while enforcing compile-time checks.[166]
csharp
using System;
using System.Collections.Generic;
public class Stack<T> where T : IComparable<T>
{
private T[] items;
private int top;
public Stack(int capacity)
{
items = new T[capacity];
top = 0;
}
public void Push(T item)
{
if (top == items.Length)
{
throw new InvalidOperationException("Stack overflow");
}
items[top++] = item;
}
public T Pop()
{
if (top == 0)
{
throw new InvalidOperationException("Stack underflow");
}
return items[--top];
}
public int Count => top;
}
using System;
using System.Collections.Generic;
public class Stack<T> where T : IComparable<T>
{
private T[] items;
private int top;
public Stack(int capacity)
{
items = new T[capacity];
top = 0;
}
public void Push(T item)
{
if (top == items.Length)
{
throw new InvalidOperationException("Stack overflow");
}
items[top++] = item;
}
public T Pop()
{
if (top == 0)
{
throw new InvalidOperationException("Stack underflow");
}
return items[--top];
}
public int Count => top;
}
This implementation demonstrates how generics parameterize the class with T, restricting it to types that implement IComparable<T> for comparability, which provides type-safe access to comparison methods without casting.[166]
To use the Stack<T> class, instantiate it with a specific type, such as int for integers, which satisfies the constraint since int implements IComparable<int>. For example:
csharp
Stack<int> intStack = new Stack<int>(5);
intStack.Push(1);
intStack.Push(2);
int value = intStack.Pop(); // Returns 2, type-safe without casting
Console.WriteLine(value); // Output: 2
Stack<int> intStack = new Stack<int>(5);
intStack.Push(1);
intStack.Push(2);
int value = intStack.Pop(); // Returns 2, type-safe without casting
Console.WriteLine(value); // Output: 2
This usage highlights the compile-time type safety of generics: the compiler ensures that only int values can be pushed or popped, preventing invalid type assignments that would otherwise occur at runtime. Additionally, for value types like int, generics avoid boxing and unboxing operations, as the internal array stores T directly rather than as object, leading to better memory efficiency and performance compared to non-generic alternatives.[167]
In contrast, the non-generic ArrayList from the System.Collections namespace stores elements as object, requiring explicit casting for retrieval and risking InvalidCastException at runtime if types mismatch. For instance, using ArrayList to simulate a stack would involve code like:
csharp
using System.Collections;
ArrayList arrayList = new ArrayList();
arrayList.Add(1); // Boxes int to object
arrayList.Add(2);
int value = (int)arrayList[arrayList.Count - 1]; // Pops via index, requires [unboxing](/page/Unboxing) and casting
using System.Collections;
ArrayList arrayList = new ArrayList();
arrayList.Add(1); // Boxes int to object
arrayList.Add(2);
int value = (int)arrayList[arrayList.Count - 1]; // Pops via index, requires [unboxing](/page/Unboxing) and casting
This approach incurs boxing overhead for value types, converting them to object on addition and unboxing on retrieval, which degrades performance and lacks the compile-time guarantees of generics. The Stack<T> class, by leveraging generics, eliminates these issues, making it preferable for type-safe, efficient reusable code.[167]
LINQ example
LINQ (Language Integrated Query) provides a declarative way to query and manipulate data collections in C#, using either method syntax with extension methods or query syntax that resembles SQL. A simple example involves filtering even numbers from a list and doubling them, demonstrating how LINQ transforms data without explicit loops.[168]
Consider a list of integers:
csharp
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
Using method syntax, the query can be written as:
csharp
var doubledEvens = numbers.Where(n => n % 2 == 0).Select(n => n * 2);
var doubledEvens = numbers.Where(n => n % 2 == 0).Select(n => n * 2);
This employs lambda expressions to define the filtering (even numbers) and projection (doubling) operations, leveraging the Where and Select standard query operators from the System.Linq namespace.[168]
Equivalently, in query syntax:
csharp
var doubledEvens = from n in numbers
where n % 2 == 0
select n * 2;
var doubledEvens = from n in numbers
where n % 2 == 0
select n * 2;
This form offers a more readable, SQL-like structure for complex queries while compiling to the same method calls under the hood.[168]
LINQ queries exhibit deferred execution, meaning the operations are not performed until the results are enumerated, such as in a foreach loop or when materializing the query into a concrete collection. For instance, calling doubledEvens.ToList() forces immediate execution and returns a List<int> containing {4, 8, 12} in this case.[168]
Beyond basic collections, LINQ integrates seamlessly with object-oriented data models, allowing queries on classes like Student or Product via properties, and supports XML manipulation through LINQ to XML for loading, querying, and transforming documents declaratively.[169]