Skip to content

Available C# and .NET features

YoshiRulz edited this page Jul 29, 2025 · 25 revisions

This document is to supplement BizHawk development, and will be deleted once the project is on .NET Core. Yoshi will maintain a copy on GitLab for the benefit of other projects.

BizHawk-specific notes:

  • All projects in /src use C# 12 currently, as do all the .NET projects in /External*Projects but new features have only been adopted in the main solution.
  • In the added "convention" column, I've used required/disallowed/encouraged/discouraged/allowed like RFC 2119's MUST / MUST NOT / SHOULD / SHOULD NOT / MAY, respectively. Unsupported is also MUST NOT (because attempting to use the feature will result in an error).
  • Each project's target (Framework vs. Standard) is at the top of its project file, or you can check the project graph.

Legend:
βœ”οΈ Available
β­• Available with polyfill
❌ Not available
? Unknown
Some features are marked βœ”οΈ despite ostensibly needing a polyfill because they are enhancements to existing features and work anywhere the base feature is availableβ€”for example, switching on a Span<char> requires no additional polyfill.
The official BCL type reference now has a "netstandard2.0 w/ polyfills" filter, though it obviously won't have third-party polyfills.

πŸ”΅ Availability in .NET Framework 4.8 (net48)
🟒 Availability in .NET Standard 2.0 (netstandard2.0)
I have not considered Framework 4.7.2 and below as there is little reason not to upgrade to Framework 4.8. I have not considered Framework 4.8.1 because it matches Framework 4.8 in terms of language features and is generally not useful. I use Standard 2.0 and not Standard 2.1 as the latter is not subsumed by Framework 4.8, rendering it useless. Presumably Mono and the Windows implementation are the same when it comes to these runtime-support-gated features, but for reference, all testing was done with Mono.

Note: .NET calls destructuring "deconstructing", not to be confused with destructing which .NET calls "finalising".

Feature πŸ”΅ net48 🟒 ns2.0 convention for main BizHawk solution
prop backing field as field βœ”οΈ βœ”οΈ waiting for .NET 10 LTS
^ C# 14 ^ πŸ”΅ net48 🟒 ns2.0 ---
partial props/indexers βœ”οΈ βœ”οΈοΈ waiting for .NET 10 LTS
allows ref struct constraint ❌ ❌ unsupported
interfaces for ref structs βœ”οΈ βœ”οΈοΈ discouraged (very useless)
ref structs and unsafe in state-machine methods ? ? presumably unsupported
escape sequence for ␛ char βœ”οΈ βœ”οΈοΈ waiting for .NET 10 LTS
sugar for lock with Lock β­• β­• should be able to use polyfill w/ C# 12
enhanced params βœ”οΈ βœ”οΈ waiting for .NET 10 LTS
^ C# 13 ^ πŸ”΅ net48 🟒 ns2.0 ---
[UnsafeAccessor] ❌ ❌ unsupported
[InlineArray] ❌ ❌ unsupported
using aliases for tuples, etc. βœ”οΈ βœ”οΈ disallowed (use a struct instead of a tuple alias and we'll consider replacing them with <Using/> later)
default arguments for lambdas βœ”οΈ βœ”οΈ discouraged (you should be using local methods)
ref readonly parameters βœ”οΈ βœ”οΈ allowed
unified Span/Array/List init syntax βœ”οΈ βœ”οΈ encouraged
primary constructors on non-records βœ”οΈ βœ”οΈ encouraged
^ C# 12 ^ πŸ”΅ net48 🟒 ns2.0 ---
user-defined checked operators βœ”οΈ βœ”οΈ encouraged, and consider marking non-checked variant as [Obsolete]
file access modifier βœ”οΈ βœ”οΈ allowed
simple ref fields ❌ ❌ unsupported
required props β­• β­• disallowed
Encoding.UTF8.GetBytes shorthand βœ”οΈ βœ”οΈ encouraged
enhanced nameof βœ”οΈ βœ”οΈ encouraged
pattern matching for Span<char> βœ”οΈ βœ”οΈ encouraged
Kotlin-like raw string literals βœ”οΈ βœ”οΈ allowed
list pattern matching βœ”οΈ βœ”οΈ encouraged
multi-line expressions in interpolated strings βœ”οΈ βœ”οΈ discouraged
generic maths using static abstract members ❌ ❌ unsupported
genericised attributes βœ”οΈ βœ”οΈ allowed
^ C# 11 ^ πŸ”΅ net48 🟒 ns2.0 ---
per-method AsyncMethodBuilder ? ? unsupported
enhanced null analysis βœ”οΈ βœ”οΈ allowed
enhanced destructuring βœ”οΈ βœ”οΈ encouraged
sealed ToString in records βœ”οΈ βœ”οΈ encouraged when records are used
limited string interpolation in consts βœ”οΈ βœ”οΈ encouraged
attributes for lambdas βœ”οΈ βœ”οΈ allowed
type inference for lambdas βœ”οΈ βœ”οΈ allowed
pattern matching IV βœ”οΈ βœ”οΈ encouraged
namespace A; βœ”οΈ βœ”οΈ disallowed (will mass-migrate)
global using A;/<Using> βœ”οΈ βœ”οΈ disallowed (will mass-migrate)
custom interpolated string handlers β­• β­• allowed
with for structs βœ”οΈ βœ”οΈ allowed
enhanced struct field init βœ”οΈ βœ”οΈ discouraged
record struct βœ”οΈ βœ”οΈ discouraged
^ C# 10 ^ πŸ”΅ net48 🟒 ns2.0 ---
enhanced partial methods βœ”οΈ βœ”οΈ allowed
[ModuleInitializer] method β­• β­• discouraged
attributes on local methods βœ”οΈ βœ”οΈ allowed
discarding lambda parameters βœ”οΈ βœ”οΈ encouraged
foreach picks up extension GetEnumerators βœ”οΈ βœ”οΈ discouraged (surely this can only be used for stupid)
covariant return type when overriding ❌ ❌ unsupported
enhanced type inference βœ”οΈ βœ”οΈ omit explicit type cast where possible, otherwise place the cast on default branch of switch and first branch of ternary
static lambdas βœ”οΈ βœ”οΈ encouraged
target-typed new() βœ”οΈ βœ”οΈ encouraged
basic function pointers βœ”οΈ βœ”οΈ encouraged
nint/nuint keywords βœ”οΈ βœ”οΈ allowed
pattern matching III βœ”οΈ βœ”οΈ encouraged
unindented Main βœ”οΈ N/A N/A (neither executable uses it)
with for records βœ”οΈ βœ”οΈ encouraged when records are used
init β­• β­• disallowed (since such accessors can write to readonly fields)
record class βœ”οΈ βœ”οΈ discouraged
^ C# 9 ^ πŸ”΅ net48 🟒 ns2.0 ---
@$"" (instead of $@"") βœ”οΈ βœ”οΈ disallowed
stackalloc as arg for Span param βœ”οΈ βœ”οΈ allowed
??= βœ”οΈ βœ”οΈ encouraged
Index and Range (^ and .. operators) β­• β­• discouraged (we also have a generic Range<T: unmanaged, IComparable<T>> but it has some problems, like not working with ..)
async streams β­• β­• unknown
NRTs (attribute-based analysis) β­• β­• encouraged
NRTs (syntax and basic analysis) βœ”οΈ βœ”οΈ encouraged for new files, see project graph for when #nullable enable is needed
static local methods βœ”οΈ βœ”οΈ encouraged
using statement without block βœ”οΈ βœ”οΈ encouraged
pattern matching II βœ”οΈ βœ”οΈ encouraged
switch expression βœ”οΈ βœ”οΈ encouraged
default interface methods ❌ ❌ unsupported
readonly methods/getters/setters βœ”οΈ βœ”οΈ encouraged
^ C# 8 ^ πŸ”΅ net48 🟒 ns2.0 ---
field attribute target for auto-prop backing field βœ”οΈ βœ”οΈ discouraged (surely this can only be used for stupid)
stackalloc with array intialiser βœ”οΈ βœ”οΈ encouraged
^ C# 7.3 ^ πŸ”΅ net48 🟒 ns2.0 ---
Span and co. β­• β­• encouraged
ref struct (stack-bound) βœ”οΈ βœ”οΈ allowed
readonly struct and in parameters βœ”οΈ βœ”οΈ encouraged
^ C# 7.2 ^ πŸ”΅ net48 🟒 ns2.0 ---
inferred tuple field names βœ”οΈ βœ”οΈ discouraged
default without type βœ”οΈ βœ”οΈ encouraged for non-nullable value types, or as default! to appease compiler when definitely assigned, discouraged otherwise
async Main ? ? N/A (neither executable uses it)
^ C# 7.1 ^ πŸ”΅ net48 🟒 ns2.0 ---
throw expression βœ”οΈ βœ”οΈ encouraged
enhanced int literals βœ”οΈ βœ”οΈ encouraged
discards βœ”οΈ βœ”οΈ encouraged
ref returns/locals βœ”οΈ βœ”οΈ allowed
expression-bodied constructors βœ”οΈ βœ”οΈ encouraged
local methods βœ”οΈ βœ”οΈ preferred over lambdas/delegates if used multiple times or to unsub from event
basic pattern matching βœ”οΈ βœ”οΈ encouraged
KeyValuePair<K, V>.Deconstruct β­• β­• allowed
basic tuples and destructuring βœ”οΈ βœ”οΈ encouraged
out var βœ”οΈ βœ”οΈ encouraged
^ C# 7.0 ^ πŸ”΅ net48 🟒 ns2.0 ---
nameof βœ”οΈ βœ”οΈ encouraged
interpolated string literals βœ”οΈ βœ”οΈ preferred over string.Format or concatenation
null-conditional member access βœ”οΈ βœ”οΈ required
expression-bodied methods/props βœ”οΈ βœ”οΈ encouraged
inline initialisation of auto-props βœ”οΈ βœ”οΈ encouraged
catch (Exception e) when (predicate(e)) βœ”οΈ βœ”οΈ encouraged
using static A; βœ”οΈ βœ”οΈ allowed only for the file's own namespace
^ C# 6 ^ πŸ”΅ net48 🟒 ns2.0 ---
[Caller*] βœ”οΈ βœ”οΈ allowed
async βœ”οΈ βœ”οΈ allowed
^ C# 5 ^ πŸ”΅ net48 🟒 ns2.0 ---
covariant and contravariant generics βœ”οΈ βœ”οΈ encouraged
default arguments βœ”οΈ βœ”οΈ preferred over overloads
named arguments βœ”οΈ βœ”οΈ encouraged for primitive-typed parameters or when 2+ parameters are of the same type
dynamic type βœ”οΈ β­• discouraged
^ C# 4 ^ πŸ”΅ net48 🟒 ns2.0 ---
object initialisation syntax βœ”οΈ βœ”οΈ encouraged
simple partial methods βœ”οΈ βœ”οΈ allowed
var βœ”οΈ βœ”οΈ preferred except when target-typed new() can be used
extension methods βœ”οΈ βœ”οΈ allowed
Expression trees βœ”οΈ βœ”οΈ discouraged (what are these even for)
lambdas βœ”οΈ βœ”οΈ preferred over delegates
LINQ's query expression syntax βœ”οΈ βœ”οΈ disallowed
anonymous classes βœ”οΈ βœ”οΈ disallowed (use tuples)
auto-props βœ”οΈ βœ”οΈ encouraged
^ C# 3 ^ πŸ”΅ net48 🟒 ns2.0 ---
anonymous delegates βœ”οΈ βœ”οΈ disallowed
^ C# 2 ^ πŸ”΅ net48 🟒 ns2.0 ---
using alias βœ”οΈ βœ”οΈ discouraged unless there's a conflict
delegate constructors βœ”οΈ βœ”οΈ disallowed
unsafe (pointers etc.) βœ”οΈ βœ”οΈ discouraged

The official C# version history is here, with this separate list for vNext.

Clone this wiki locally