- Raw string literals: introduces a string literal where the content never needs escaping (
var json = """{ "summary": "text" }""";
orvar json = $$"""{ "summary": "text", "length": {{length}} }""";
). - UTF-8 string literals: UTF-8 string literals with the
u8
suffix (ReadOnlySpan<byte> s = "hello"u8;
) - Pattern match
Span<char>
on a constant string: an input value of typeSpan<char>
orReadonlySpan<char>
can be matched with a constant string pattern (span is "123"
). - Newlines in interpolations: allows newline characters in single-line interpolated strings.
- List patterns: allows matching indexable types (
list is [1, 2, ..]
). - File-local types: introduces the
file
type modifier (file class C { ... }
). - Ref fields: allows
ref
field declarations in aref struct
(ref struct S { ref int field; ... }
), introducesscoped
modifier and[UnscopedRef]
attribute. - Required members: introduces the
required
field and property modifier and[SetsRequiredMembers]
attribute. - Static abstract members in interfaces: allows an interface to specify abstract static members.
- Unsigned right-shift operator: introduces the
>>>
operator and>>>=
. checked
user-defined operators: numeric and conversion operators support definingchecked
variants (public static Int128 operator checked +(Int128 lhs, Int128 rhs) { ... }
).- Relaxing shift operator requirements: the right-hand-side operand of a shift operator is no longer restricted to only be
int
- Numeric IntPtr:
nint
/nuint
become simple types aliasingSystem.IntPtr
/System.UIntPtr
. - Auto-default structs: struct constructors automatically default fields that are not explicitly assigned.
- Generic attributes: allows attributes to be generic (
[MyAttribute<int>]
). - Extended
nameof
scope in attributes: allowsnameof(parameter)
inside an attribute on a method or parameter ([MyAttribute(nameof(parameter))] void M(int parameter) { }
).
- Record structs (
record struct Point(int X, int Y);
,var newPoint = point with { X = 100 };
). - With expression on structs and anonymous types.
- Global using directives:
global using
directives avoid repeating the sameusing
directives across many files in your program. - Improved definite assignment: definite assignment and nullability analysis better handle common patterns such as
dictionary?.TryGetValue(key, out value) == true
. - Constant interpolated strings: interpolated strings composed of constants are themselves constants.
- Extended property patterns: property patterns allow accessing nested members (
if (e is MethodCallExpression { Method.Name: "MethodName" })
). - Sealed record ToString: a record can inherit a base record with a sealed
ToString
. - Incremental source generators: improve the source generation experience in large projects by breaking down the source generation pipeline and caching intermediate results.
- Mixed deconstructions: deconstruction-assignments and deconstruction-declarations can be blended together (
(existingLocal, var declaredLocal) = expression
). - Method-level AsyncMethodBuilder: the AsyncMethodBuilder used to compile an
async
method can be overridden locally. - #line span directive: allow source generators like Razor fine-grained control of the line mapping with
#line
directives that specify the destination span (#line (startLine, startChar) - (endLine, endChar) charOffset "fileName"
). - Lambda improvements: attributes and return types are allowed on lambdas; lambdas and method groups have a natural delegate type (
var f = short () => 1;
). - Interpolated string handlers: interpolated string handler types allow efficient formatting of interpolated strings in assignments and invocations.
- File-scoped namespaces: files with a single namespace don't need extra braces or indentation (
namespace X.Y.Z;
). - Parameterless struct constructors: support parameterless constructors and instance field initializers for struct types.
- CallerArgumentExpression: this attribute allows capturing the expressions passed to a method as strings.
- Records and
with
expressions: succinctly declare reference types with value semantics (record Point(int X, int Y);
,var newPoint = point with { X = 100 };
). - Init-only setters: init-only properties can be set during object creation (
int Property { get; init; }
). - Top-level statements: the entry point logic of a program can be written without declaring an explicit type or
Main
method. - Pattern matching enhancements: relational patterns (
is < 30
), combinator patterns (is >= 0 and <= 100
,case 3 or 4:
,is not null
), parenthesized patterns (is int and (< 0 or > 100)
), type patterns (case Type:
). - Native sized integers: the numeric types
nint
andnuint
match the platform memory size. - Function pointers: enable high-performance code leveraging IL instructions
ldftn
andcalli
(delegate* <int, void> local;
) - Suppress emitting
localsinit
flag: attributing a method with[SkipLocalsInit]
will suppress emitting thelocalsinit
flag to reduce cost of zero-initialization. - Target-typed new expressions:
Point p = new(42, 43);
. - Static anonymous functions: ensure that anonymous functions don't capture
this
or local variables (static () => { ... };
). - Target-typed conditional expressions: conditional expressions which lack a natural type can be target-typed (
int? x = b ? 1 : null;
). - Covariant return types: a method override on reference types can declare a more derived return type.
- Lambda discard parameters: multiple parameters
_
appearing in a lambda are allowed and are discards. - Attributes on local functions.
- Module initializers: a method attributed with
[ModuleInitializer]
will be executed before any other code in the assembly. - Extension
GetEnumerator
: an extensionGetEnumerator
method can be used in aforeach
. - Partial methods with returned values: partial methods can have any accessibility, return a type other than
void
and useout
parameters, but must be implemented. - Source Generators
- Nullable reference types: express nullability intent on reference types with
?
,notnull
constraint and annotations attributes in APIs, the compiler will use those to try and detect possiblenull
values being dereferenced or passed to unsuitable APIs. - Default interface members: interfaces can now have members with default implementations, as well as static/private/protected/internal members except for state (ie. no fields).
- Recursive patterns: positional and property patterns allow testing deeper into an object, and switch expressions allow for testing multiple patterns and producing corresponding results in a compact fashion.
- Async streams:
await foreach
andawait using
allow for asynchronous enumeration and disposal ofIAsyncEnumerable<T>
collections andIAsyncDisposable
resources, and async-iterator methods allow convenient implementation of such asynchronous streams. - Enhanced using: a
using
declaration is added with an implicit scope andusing
statements and declarations allow disposal ofref
structs using a pattern. - Ranges and indexes: the
i..j
syntax allows constructingSystem.Range
instances, the^k
syntax allows constructingSystem.Index
instances, and those can be used to index/slice collections. - Null-coalescing assignment:
??=
allows conditionally assigning when the value is null. - Static local functions: local functions modified with
static
cannot capturethis
or local variables, and local function parameters now shadow locals in parent scopes. - Unmanaged generic structs: generic struct types that only have unmanaged fields are now considered unmanaged (ie. they satisfy the
unmanaged
constraint). - Readonly members: individual members can now be marked as
readonly
to indicate and enforce that they do not modify instance state. - Stackalloc in nested contexts:
stackalloc
expressions are now allowed in more expression contexts. - Alternative interpolated verbatim strings:
@$"..."
strings are recognized as interpolated verbatim strings just like$@"..."
. - Obsolete on property accessors: property accessors can now be individually marked as obsolete.
- Permit
t is null
on unconstrained type parameter
System.Enum
,System.Delegate
andunmanaged
constraints.- Ref local re-assignment: Ref locals and ref parameters can now be reassigned with the ref assignment operator (
= ref
). - Stackalloc initializers: Stack-allocated arrays can now be initialized, e.g.
Span<int> x = stackalloc[] { 1, 2, 3 };
. - Indexing movable fixed buffers: Fixed buffers can be indexed into without first being pinned.
- Custom
fixed
statement: Types that implement a suitableGetPinnableReference
can be used in afixed
statement. - Improved overload candidates: Some overload resolution candidates can be ruled out early, thus reducing ambiguities.
- Expression variables in initializers and queries: Expression variables like
out var
and pattern variables are allowed in field initializers, constructor initializers and LINQ queries. - Tuple comparison: Tuples can now be compared with
==
and!=
. - Attributes on backing fields: Allows
[field: …]
attributes on an auto-implemented property to target its backing field.
C# 7.2 - Visual Studio 2017 version 15.5
- Span and ref-like types
- In parameters and readonly references
- Ref conditional
- Non-trailing named arguments
- Private protected accessibility
- Digit separator after base specifier
C# 7.1 - Visual Studio 2017 version 15.3
- Async main
- Default expressions
- Reference assemblies
- Inferred tuple element names
- Pattern-matching with generics
C# 7.0 - Visual Studio 2017
- Out variables
- Pattern matching
- Tuples
- Deconstruction
- Discards
- Local Functions
- Binary Literals
- Digit Separators
- Ref returns and locals
- Generalized async return types
- More expression-bodied members
- Throw expressions
C# 6 - Visual Studio 2015
- Draft Specification online
- Compiler-as-a-service (Roslyn)
- Import of static type members into namespace
- Exception filters
- Await in catch/finally blocks
- Auto property initializers
- Default values for getter-only properties
- Expression-bodied members
- Null propagator (null-conditional operator, succinct null checking)
- String interpolation
- nameof operator
- Dictionary initializer
C# 5 - Visual Studio 2012
- Asynchronous methods
- Caller info attributes
- foreach loop was changed to generates a new loop variable rather than closing over the same variable every time
C# 4 - Visual Studio 2010
- Dynamic binding
- Named and optional arguments
- Co- and Contra-variance for generic delegates and interfaces
- Embedded interop types ("NoPIA")
C# 3 - Visual Studio 2008
- Implicitly typed local variables
- Object and collection initializers
- Auto-Implemented properties
- Anonymous types
- Extension methods
- Query expressions, a.k.a LINQ (Language Integrated Query)
- Lambda expression
- Expression trees
- Partial methods
- Lock statement
C# 2 - Visual Studio 2005
- Generics
- Partial types
- Anonymous methods
- Iterators, a.k.a yield statement
- Nullable types
- Getter/setter separate accessibility
- Method group conversions (delegates)
- Static classes
- Delegate inference
- Type and namespace aliases
- Covariance and contravariance
C# 1.2 - Visual Studio .NET 2003
- Dispose in foreach
- foreach over string specialization
C# 1.0 - Visual Studio .NET 2002
- Classes
- Structs
- Enums
- Interfaces
- Events
- Operator overloading
- User-defined conversion operators
- Properties
- Indexers
- Output parameters (out and ref)
params
arrays- Delegates
- Expressions
- using statement
- goto statement
- Preprocessor directives
- Unsafe code and pointers
- Attributes
- Literals
- Verbatim identifier
- Unsigned integer types
- Boxing and unboxing