ResXResourceWriter is a verbatim port of the Mono implementation
(see file header). Both warnings flag deliberate decisions in the
upstream port that we preserve for fidelity:
* CA1063 — Dispose() is virtual and the protected Dispose(bool) is
not, the inverse of the canonical pattern; keeping the Mono shape.
* CA2213 — the writer's stream / textwriter fields are caller-owned
and intentionally not disposed by the writer.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Four cases where the analyzer rule conflicts with intentional design:
* EmptyList<T>.IDisposable.Dispose (CA1063) — explicit IDisposable on
IEnumerator<T>; making it public would conflict with the rest of the
IList<T> / IEnumerator<T> surface.
* MetadataFile.SectionHeaders (CA1065) — throw documents that this
MetadataFileKind has no PE sections; PE-like derived kinds override.
* LongSet.GetHashCode + LongSet itself (CA1065 + CA2231) — explicit
guards against using LongSet in hash containers / via equality
operators; SetEquals is the supported comparison and
IEquatable<LongSet>.Equals is itself [Obsolete].
* AnnotationList.Clone (CA2002) — AnnotationList is a private nested
type; the surrounding Annotatable class deliberately locks on the
AnnotationList instance to serialize annotation reads/writes, and
external code cannot obtain a reference to it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MetadataFile now declares IDisposable using the canonical pattern
(public non-virtual Dispose() + protected virtual Dispose(bool)).
PEFile and WebCilFile become sealed and override Dispose(bool) to
release the PEReader and MemoryMappedViewAccessor they own;
ResourcesFile is also sealed. PortableDebugInfoProvider disposes the
MetadataReaderProvider it owns. LoadedAssembly implements IDisposable
and disposes both the loaded MetadataFile and the debug-info provider.
AssemblyList.Unload / Clear / ReloadAssembly / HotReplaceAssembly now
dispose the LoadedAssembly instances they evict, fixing a resource leak
where every "Reload Assembly" held the previous PEReader (and the
underlying file handle / memory-mapped view) alive until GC eventually
finalized it.
The disposal contract terminates at the AssemblyList tier: downstream
holders of MetadataFile (MetadataModule, DecompilerTypeSystem,
AssemblyListSnapshot, ...) hold borrowed references rather than owned
ones, so making the base IDisposable does not cascade into CA1001 /
CA2213 warnings elsewhere.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
In newer runtime versions, MemoryStream is no longer a serializable type like it was in .NET Framework. This means explicit support has to be implemented for it.
DecodeArrayInitializer - Instead of relying on the Add method of a list to expand the underlying array when necessary, the code now allocates a big enough array to fit all the elements removing the need for the Add method to expand the array several times.
BlockFromInitializer now reuses a single instance of List<ILInstruction> instead of reallocating a new one every time and clears it when necessary. The same pre-allocation approach mentioned above has been implemented here too.
Also, move the .resources->.resx conversion into the base class.
Now that we have our own ResXWriter implementation, there's no longer any reason to keep this specific to ILSpy.