-
Notifications
You must be signed in to change notification settings - Fork 390
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.
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.