-
Notifications
You must be signed in to change notification settings - Fork 430
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
/srcuse C# 12 currently, as do all the .NET projects in/External*Projectsbut 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.
Seen something on this wiki page that can be improved? Only repo members can edit directly; please reach out on Discord or the TASVideos forums (and not this repo's issue tracker).