-
Notifications
You must be signed in to change notification settings - Fork 401
Available C# and .NET features
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, switch
ing 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 struct s |
✔️ | ✔️️ | discouraged (very useless) |
ref struct s 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-record s |
✔️ | ✔️ | encouraged |
^ C# 12 ^ | 🔵 net48
|
🟢 ns2.0
|
--- |
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 GetEnumerator s |
✔️ | ✔️ | 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 |
⭕ | ⭕ | discouraged |
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.