From e39f8ab984eb38b6a2c54327f8d47239786af90f Mon Sep 17 00:00:00 2001 From: YoshiRulz Date: Fri, 23 May 2025 03:16:32 +1000 Subject: [PATCH] Remove langcode from MSDN links --- C#-and-.NET-docs-supplement.md | 40 +++++++++++++++++----------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/C#-and-.NET-docs-supplement.md b/C#-and-.NET-docs-supplement.md index a632d5d..b411c16 100644 --- a/C#-and-.NET-docs-supplement.md +++ b/C#-and-.NET-docs-supplement.md @@ -9,26 +9,26 @@ Under .NET 8 (all of this is untested, just based on the docs and .NET issue tra - The largest 1D byte array is `new byte[Array.MaxLength]`, `Array.MaxLength` being [hardcoded to `0x7FFF_FFC7`](https://github.com/dotnet/runtime/blob/5535e31a712343a63f5d7d796cd874e563e5ac14/src/libraries/System.Private.CoreLib/src/System/Array.cs#L2135-L2137) or just under 2 GiB. (Would probably want to alloc in 1 GiB chunks to have nice code, maybe even smaller to keep the GC happy.) - The largest 1D struct array is `new T[Array.MaxLength]`. So its size is... unbounded since structs don't have a size limit apart from the stack size (and `[InlineArray]`'s poorly-documented 1 MiB cap). (TODO check there isn't an undocumented cap on struct size) - The largest *n*-D byte array is `UNK()` (`LongLength` is `0xUNK` or UNK GiB). -- If a call would allocate beyond those limits (or the process' or machine's limits), an `OutOfMemoryException` will be thrown, which can be caught and handled [but definitely shouldn't be](https://learn.microsoft.com/en-us/dotnet/api/system.outofmemoryexception?view=net-8.0#remarks). +- If a call would allocate beyond those limits (or the process' or machine's limits), an `OutOfMemoryException` will be thrown, which can be caught and handled [but definitely shouldn't be](https://learn.microsoft.com/dotnet/api/system.outofmemoryexception?view=net-8.0#remarks). - All of that is on the managed heap. The default/global stack is OS-specific and [cannot be changed](https://github.com/dotnet/runtime/issues/107183): On Windows, it's 1.5 MiB [due to an oversight](https://github.com/dotnet/runtime/issues/96347#issuecomment-1871528297), and on Linux, it's inherited from the OS, [typically 8 MiB](https://github.com/dotnet/runtime/issues/33622#issuecomment-599462300). - - The stack size for managed threads can be [specified on init](https://learn.microsoft.com/en-us/dotnet/api/system.threading.thread.-ctor?view=net-8.0#system-threading-thread-ctor(system-threading-threadstart-system-int32)), though as with all of this you may have an XY problem and should reconsider. - - If a call would allocate beyond the stack size (or a call chain grows out of control), a `StackOverflowException` will be thrown, which [cannot be caught](https://learn.microsoft.com/en-us/dotnet/api/system.stackoverflowexception?view=net-8.0#remarks). + - The stack size for managed threads can be [specified on init](https://learn.microsoft.com/dotnet/api/system.threading.thread.-ctor?view=net-8.0#system-threading-thread-ctor(system-threading-threadstart-system-int32)), though as with all of this you may have an XY problem and should reconsider. + - If a call would allocate beyond the stack size (or a call chain grows out of control), a `StackOverflowException` will be thrown, which [cannot be caught](https://learn.microsoft.com/dotnet/api/system.stackoverflowexception?view=net-8.0#remarks). Under Mono (x64 unless specified and without `gcAllowVeryLargeObjects`, TODO see if Mono respects that): - The largest 1D byte array is `new byte[int.MaxValue]` (just under 2 GiB), as there is no way to instantiate an array with a larger size (`Array.CreateInstance(typeof(T), 0x8000_0000L)` will throw `ArgumentOutOfRangeException: Arrays larger than 2GB are not supported.`, which I suppose is technically incorrect). - Similarly, the largest 1D struct array is `new T[int.MaxValue]`. So its size is... unbounded since structs on the heap don't have a size limit. - The largest *n*-D byte array is `new byte[2, 0x7FFF_FFE6]` (`LongLength` is `0xFFFF_FFCC` or just under 4 GiB). Allocating a single byte more gives an OoME. Multidimensional arrays of other structs appear to also be limited to `0xFFFF_FFCC` octets. -- If a call would allocate beyond those limits (or the process' or machine's limits), the relevant builtin method will throw an `OutOfMemoryException`, which can be caught and handled [but definitely shouldn't be](https://learn.microsoft.com/en-us/dotnet/api/system.outofmemoryexception?view=netframework-4.8#remarks). +- If a call would allocate beyond those limits (or the process' or machine's limits), the relevant builtin method will throw an `OutOfMemoryException`, which can be caught and handled [but definitely shouldn't be](https://learn.microsoft.com/dotnet/api/system.outofmemoryexception?view=netframework-4.8#remarks). - All of that is on the managed heap. The default/global stack is UNK MiB large and can be changed [externally with `ulimit`](https://github.com/dotnet/runtime/issues/96347#issuecomment-1981511546) (a builtin in most POSIX shells, including BASH), but not with Roslyn. - - The stack size for managed threads can be [specified on init](https://learn.microsoft.com/en-us/dotnet/api/system.threading.thread.-ctor?view=netframework-4.8#system-threading-thread-ctor(system-threading-threadstart-system-int32)), though as with all of this you may have an XY problem and should reconsider. The max. is [1 MiB / 2 MiB (32-bit/64-bit hosts)](https://stackoverflow.com/a/19909421), and there is a [hack to bypass the limit](https://stackoverflow.com/a/48003390) (untested). - - If a `stackalloc` call would allocate beyond those limits (with `stackalloc`; TODO check this), or a call chain grows out of control, a `StackOverflowException` will be thrown, which [cannot be caught](https://learn.microsoft.com/en-us/dotnet/api/system.stackoverflowexception?view=netframework-4.8#remarks). + - The stack size for managed threads can be [specified on init](https://learn.microsoft.com/dotnet/api/system.threading.thread.-ctor?view=netframework-4.8#system-threading-thread-ctor(system-threading-threadstart-system-int32)), though as with all of this you may have an XY problem and should reconsider. The max. is [1 MiB / 2 MiB (32-bit/64-bit hosts)](https://stackoverflow.com/a/19909421), and there is a [hack to bypass the limit](https://stackoverflow.com/a/48003390) (untested). + - If a `stackalloc` call would allocate beyond those limits (with `stackalloc`; TODO check this), or a call chain grows out of control, a `StackOverflowException` will be thrown, which [cannot be caught](https://learn.microsoft.com/dotnet/api/system.stackoverflowexception?view=netframework-4.8#remarks). Under .NET Framework on Windows (x64 unless specified): -- Untested, but I'm guessing it's probably the same as Mono so long as the [`gcAllowVeryLargeObjects`](https://learn.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/runtime/gcallowverylargeobjects-element) is enabled in the runtime config, going by those docs and [this blog post](https://www.centerspace.net/large-matrices-and-vectors). +- Untested, but I'm guessing it's probably the same as Mono so long as the [`gcAllowVeryLargeObjects`](https://learn.microsoft.com/dotnet/framework/configure-apps/file-schema/runtime/gcallowverylargeobjects-element) is enabled in the runtime config, going by those docs and [this blog post](https://www.centerspace.net/large-matrices-and-vectors). - Or maybe not; see [this SO answer](https://stackoverflow.com/a/34413257) and the comment below it. - The default/global stack is 1 MiB, specified in a PE header. This can be changed [externally with `EDITBIN`](https://stackoverflow.com/a/54584830), but not with Roslyn nor at runtime in any way. - - The stack size for managed threads [defaults to 1 MiB / 4 MiB (x86/x64)](https://stackoverflow.com/a/5507910) (TODO surely there's an official source) can be [specified on init](https://learn.microsoft.com/en-us/dotnet/api/system.threading.thread.-ctor?view=netframework-4.8#system-threading-thread-ctor(system-threading-threadstart-system-int32)), though as with all of this you may have an XY problem and should reconsider. The max. is UNK MiB (default AppDomain; 1 MiB if untrusted) and the min. [seems to be OS-dependent](https://learn.microsoft.com/en-us/dotnet/api/system.threading.thread.-ctor?view=netframework-4.8#system-threading-thread-ctor(system-threading-threadstart-system-int32)) but was [~256 KiB on Vista](https://techcommunity.microsoft.com/t5/windows-blog-archive/pushing-the-limits-of-windows-processes-and-threads/ba-p/723824) and presumably hasn't been changed since. - - If a call would allocate beyond the stack size (or a call chain grows out of control), a `StackOverflowException` will be thrown, which [cannot be caught](https://learn.microsoft.com/en-us/dotnet/api/system.stackoverflowexception?view=netframework-4.8#remarks). + - The stack size for managed threads [defaults to 1 MiB / 4 MiB (x86/x64)](https://stackoverflow.com/a/5507910) (TODO surely there's an official source) can be [specified on init](https://learn.microsoft.com/dotnet/api/system.threading.thread.-ctor?view=netframework-4.8#system-threading-thread-ctor(system-threading-threadstart-system-int32)), though as with all of this you may have an XY problem and should reconsider. The max. is UNK MiB (default AppDomain; 1 MiB if untrusted) and the min. [seems to be OS-dependent](https://learn.microsoft.com/dotnet/api/system.threading.thread.-ctor?view=netframework-4.8#system-threading-thread-ctor(system-threading-threadstart-system-int32)) but was [~256 KiB on Vista](https://techcommunity.microsoft.com/t5/windows-blog-archive/pushing-the-limits-of-windows-processes-and-threads/ba-p/723824) and presumably hasn't been changed since. + - If a call would allocate beyond the stack size (or a call chain grows out of control), a `StackOverflowException` will be thrown, which [cannot be caught](https://learn.microsoft.com/dotnet/api/system.stackoverflowexception?view=netframework-4.8#remarks). - It should be no surprise that all these limits were inherited from native Win32. Thankfully modern .NET is deviating from that where necessary. ## BCL source @@ -41,18 +41,18 @@ The API reference on Microsoft Learn (formerly MSDN) now links to the source in ## Const (byte/primitive) arrays Not allowed as either array nor `Span`, despite string literals now effectively having the type `const ReadOnlySpan`, -and despite arrays of primitive types being [allowed for attribute parameters](https://learn.microsoft.com/en-us/dotnet/csharp/advanced-topics/reflection-and-attributes/attribute-tutorial#add-attributes-to-code) since forever. +and despite arrays of primitive types being [allowed for attribute parameters](https://learn.microsoft.com/dotnet/csharp/advanced-topics/reflection-and-attributes/attribute-tutorial#add-attributes-to-code) since forever. Use `static readonly` and weep. ## Const structs -Not allowed, even if they meet the [criteria for unmanaged types](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/unmanaged-types) and are littered with [explicit layout attributes](https://learn.microsoft.com/en-us/dotnet/standard/native-interop/customize-struct-marshalling). +Not allowed, even if they meet the [criteria for unmanaged types](https://learn.microsoft.com/dotnet/csharp/language-reference/builtin-types/unmanaged-types) and are littered with [explicit layout attributes](https://learn.microsoft.com/dotnet/standard/native-interop/customize-struct-marshalling). First-class'd structs are no different, so fields of type `ValueTuple`, `Range`, and as noted above, `Span` cannot be `const`. Also `ref structs`, which makes slightly more sense. That's probably only because you'd be able to get a reference to a `ref struct` on the heap, something which shouldn't exist, by using reflection. ## Deceptive collection type names -`IReadOnly{Collection,Dictionary,List,Set}` are for getting read-only views of the collections that implement them. They do not mean the collection is immutable (there are [separate classes](https://learn.microsoft.com/en-us/dotnet/api/system.collections.immutable?view=net-6.0) for that). The same goes for `ReadOnlySpan`. +`IReadOnly{Collection,Dictionary,List,Set}` are for getting read-only views of the collections that implement them. They do not mean the collection is immutable (there are [separate classes](https://learn.microsoft.com/dotnet/api/system.collections.immutable?view=net-6.0) for that). The same goes for `ReadOnlySpan`. > Kotlin got this right by calling its interfaces e.g. `List`/`MutableList` instead of `IReadOnlyList`/`IList`. (And it also fixed the inheritance hierarchy.) @@ -117,12 +117,12 @@ public static ReadOnlySpan UnsafeDowncast(ReadOnlySpan s ## MSBuild `Condition` placement On (older versions of?) VS, `Condition` is ignored if placed on a property/item. -It's also limited to a certain subset of expressions when placed on a `` (according to [docs](https://learn.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#adding-a-packagereference-condition) circa .NET 8). +It's also limited to a certain subset of expressions when placed on a `` (according to [docs](https://learn.microsoft.com/nuget/consume-packages/package-references-in-project-files#adding-a-packagereference-condition) circa .NET 8). Create a dedicated ``/``. ## MSBuild project evaluation (without build) -[`dotnet publish --getProperty`](https://learn.microsoft.com/en-us/visualstudio/msbuild/evaluate-items-and-properties#use-command-line-options-to-output-values) +[`dotnet publish --getProperty`](https://learn.microsoft.com/visualstudio/msbuild/evaluate-items-and-properties#use-command-line-options-to-output-values) Example: ``` @@ -147,7 +147,7 @@ If a string literal contains a date, *even if it's being deserialised to a strin ## NuGet resources are all for old CLI -[up-to-date docs on MSDN](https://learn.microsoft.com/en-us/nuget/consume-packages/install-use-packages-dotnet-cli) +[up-to-date docs on MSDN](https://learn.microsoft.com/nuget/consume-packages/install-use-packages-dotnet-cli) `dotnet list $PWD/BizHawk.sln package --outdated` will list outdated ``s (betas are **ignored** without `--include-prerelease`—if there are only betas published, it sees no releases and prints "Not found"). There is no built-in command for updating them automatically. @@ -168,7 +168,7 @@ See [dotnet/csharplang#53622](https://github.com/dotnet/csharplang/issues/53622) ## Preprocessor TFM constants and .NET Standard -The table [here](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/preprocessor-directives#conditional-compilation) is good for reference, but mind the note hidden at the bottom: +The table [here](https://learn.microsoft.com/dotnet/csharp/language-reference/preprocessor-directives#conditional-compilation) is good for reference, but mind the note hidden at the bottom: > The `NETSTANDARD__OR_GREATER` symbols are only defined for .NET Standard targets, and not for targets that implement .NET Standard [...] That is, you must use `#if !(NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER)` and not just `#if !NETSTANDARD2_1_OR_GREATER`. @@ -185,11 +185,11 @@ It seems that `Guid`'s implementation *is* stable across instances, and even acr ## `System.Drawing.Color.*` rendered -[Docs for `Color`](https://learn.microsoft.com/en-us/dotnet/api/system.drawing.color?view=netframework-4.8) don't include any pictures, so [here's a nice chart](https://user-images.githubusercontent.com/13409956/226146254-9a6fc384-c822-41e8-ae6a-3106f9e2986c.svg). +[Docs for `Color`](https://learn.microsoft.com/dotnet/api/system.drawing.color?view=netframework-4.8) don't include any pictures, so [here's a nice chart](https://user-images.githubusercontent.com/13409956/226146254-9a6fc384-c822-41e8-ae6a-3106f9e2986c.svg). ## `System.Drawing.SystemIcons` rendered -[Docs for `SystemIcons`](https://learn.microsoft.com/en-us/dotnet/api/system.drawing.systemicons?view=netframework-4.8) don't include any pictures, so here they are (Win10, Mono 6.12.x): +[Docs for `SystemIcons`](https://learn.microsoft.com/dotnet/api/system.drawing.systemicons?view=netframework-4.8) don't include any pictures, so here they are (Win10, Mono 6.12.x): ![SystemIcons_Win10](https://user-images.githubusercontent.com/13409956/217321727-b3527c87-3ad9-44bb-8276-01afde65a939.png) ![SystemIcons_Mono](https://user-images.githubusercontent.com/13409956/217311223-0d6acd76-203b-4694-9f45-4e4b8acc184b.png) @@ -198,7 +198,7 @@ Notice also the default window icon (`Form.Icon`): on Windows, it's a distinct i ## `[ThreadStatic]` field initialisation -Per [docs](https://learn.microsoft.com/en-us/dotnet/api/system.threadstaticattribute?view=netstandard-2.0#remarks) ([simpler](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2019#rule-description)), static fields initialisation is moved to the static constructor in IL, which runs on at most 1 thread, so a `[ThreadStatic]` field will be `default` on all other threads if initialised in the usual way. +Per [docs](https://learn.microsoft.com/dotnet/api/system.threadstaticattribute?view=netstandard-2.0#remarks) ([simpler](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2019#rule-description)), static fields initialisation is moved to the static constructor in IL, which runs on at most 1 thread, so a `[ThreadStatic]` field will be `default` on all other threads if initialised in the usual way. Incorrect usage in BizHawk *should* be flagged with CA2019, but apparently it's not working in CI. @@ -210,7 +210,7 @@ If an object being the wrong type is *exceptional*—the method can't handle it ## Type constraints (`where` clauses) -`class` in `where` clauses does not mean "not abstract", it means "reference type". Similarly, `struct` means "value type". There's a lot of complexity re: nullability, so [check the docs](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters) if you're writing a generic method. +`class` in `where` clauses does not mean "not abstract", it means "reference type". Similarly, `struct` means "value type". There's a lot of complexity re: nullability, so [check the docs](https://learn.microsoft.com/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters) if you're writing a generic method. TODO euler diagram