diff --git a/AGENTS.md b/AGENTS.md
index 794808542..ef872d0c8 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -204,6 +204,7 @@ SharpCompress supports multiple archive and compression formats:
- Preserve existing public method signatures and behavior when possible.
- If a breaking change is unavoidable, document it and provide a migration path.
- Add or update tests that cover backward compatibility expectations.
+- Avoid exposing public `init` setters, positional records, `required` members, or other metadata that forces consumers onto newer C# language versions; validate older-consumer compatibility with tests when changing exported APIs.
### Stream Ownership and Position Checklist
- Verify `LeaveStreamOpen` behavior for externally owned streams.
diff --git a/Directory.Packages.props b/Directory.Packages.props
index dff2b3320..ca0f52608 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -18,5 +18,6 @@
Include="Microsoft.VisualStudio.Threading.Analyzers"
Version="17.14.15"
/>
+
diff --git a/build/packages.lock.json b/build/packages.lock.json
index 395d8f2a4..e0b23c843 100644
--- a/build/packages.lock.json
+++ b/build/packages.lock.json
@@ -39,6 +39,12 @@
"resolved": "17.14.15",
"contentHash": "mXQPJsbuUD2ydq4/ffd8h8tSOFCXec+2xJOVNCvXjuMOq/+5EKHq3D2m2MC2+nUaXeFMSt66VS/J4HdKBixgcw=="
},
+ "PolySharp": {
+ "type": "Direct",
+ "requested": "[1.15.0, )",
+ "resolved": "1.15.0",
+ "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g=="
+ },
"SimpleExec": {
"type": "Direct",
"requested": "[13.0.0, )",
diff --git a/src/SharpCompress/Archives/ArchiveInformation.cs b/src/SharpCompress/Archives/ArchiveInformation.cs
index adc0dfa2b..75d3cb5a6 100644
--- a/src/SharpCompress/Archives/ArchiveInformation.cs
+++ b/src/SharpCompress/Archives/ArchiveInformation.cs
@@ -10,13 +10,29 @@ namespace SharpCompress.Archives;
///
/// to obtain an instance of this record.
///
-///
-/// The type of archive detected, or when the format is not a registered well-known type.
-///
-///
-/// when this archive format supports random access via the API,
-/// meaning the full file listing can be retrieved without decompressing the entire archive.
-/// when only the API is available,
-/// which reads entries sequentially and can only report per-entry progress.
-///
-public record ArchiveInformation(ArchiveType? Type, bool SupportsRandomAccess);
+public record ArchiveInformation
+{
+ ///
+ /// The type of archive detected, or when the format is not a registered well-known type.
+ ///
+ public ArchiveType? Type { get; set; }
+
+ ///
+ /// when this archive format supports random access via the API,
+ /// meaning the full file listing can be retrieved without decompressing the entire archive.
+ /// when only the API is available,
+ /// which reads entries sequentially and can only report per-entry progress.
+ ///
+ public bool SupportsRandomAccess { get; set; }
+
+ ///
+ /// Creates a new archive information instance.
+ ///
+ /// The detected archive type.
+ /// Whether the detected format supports random access.
+ public ArchiveInformation(ArchiveType? type, bool supportsRandomAccess)
+ {
+ Type = type;
+ SupportsRandomAccess = supportsRandomAccess;
+ }
+}
diff --git a/src/SharpCompress/Common/ExtractionOptions.cs b/src/SharpCompress/Common/ExtractionOptions.cs
index ea84ed53e..8a3b4859a 100644
--- a/src/SharpCompress/Common/ExtractionOptions.cs
+++ b/src/SharpCompress/Common/ExtractionOptions.cs
@@ -7,7 +7,7 @@ namespace SharpCompress.Common;
/// Options for configuring extraction behavior when extracting archive entries.
///
///
-/// This class is immutable. Use the with expression to create modified copies:
+/// Configure extraction behavior with constructors, property setters, or the with expression:
///
/// var options = new ExtractionOptions { Overwrite = false };
/// options = options with { PreserveFileTime = true };
@@ -19,24 +19,24 @@ public sealed record ExtractionOptions : IExtractionOptions
/// Overwrite target if it exists.
/// Breaking change: Default changed from false to true in version 0.40.0.
///
- public bool Overwrite { get; init; } = true;
+ public bool Overwrite { get; set; } = true;
///
/// Extract with internal directory structure.
/// Breaking change: Default changed from false to true in version 0.40.0.
///
- public bool ExtractFullPath { get; init; } = true;
+ public bool ExtractFullPath { get; set; } = true;
///
/// Preserve file time.
/// Breaking change: Default changed from false to true in version 0.40.0.
///
- public bool PreserveFileTime { get; init; } = true;
+ public bool PreserveFileTime { get; set; } = true;
///
/// Preserve windows file attributes.
///
- public bool PreserveAttributes { get; init; }
+ public bool PreserveAttributes { get; set; }
///
/// Delegate for writing symbolic links to disk.
@@ -44,10 +44,10 @@ public sealed record ExtractionOptions : IExtractionOptions
/// The second parameter is the target path (what the symlink refers to).
///
///
- /// Breaking change: Changed from field to init-only property in version 0.40.0.
+ /// Breaking change: Changed from field to property in version 0.40.0.
/// If no handler is provided, symbolic links are silently skipped during extraction.
///
- public Action? SymbolicLinkHandler { get; init; }
+ public Action? SymbolicLinkHandler { get; set; }
///
/// Creates a new ExtractionOptions instance with default values.
diff --git a/src/SharpCompress/Common/Options/IEncodingOptions.cs b/src/SharpCompress/Common/Options/IEncodingOptions.cs
index d9020a468..47fb56f89 100644
--- a/src/SharpCompress/Common/Options/IEncodingOptions.cs
+++ b/src/SharpCompress/Common/Options/IEncodingOptions.cs
@@ -2,5 +2,5 @@ namespace SharpCompress.Common.Options;
public interface IEncodingOptions
{
- IArchiveEncoding ArchiveEncoding { get; init; }
+ IArchiveEncoding ArchiveEncoding { get; set; }
}
diff --git a/src/SharpCompress/Common/Options/IExtractionOptions.cs b/src/SharpCompress/Common/Options/IExtractionOptions.cs
index 3e88d2473..8aac6a651 100644
--- a/src/SharpCompress/Common/Options/IExtractionOptions.cs
+++ b/src/SharpCompress/Common/Options/IExtractionOptions.cs
@@ -11,29 +11,29 @@ public interface IExtractionOptions
/// Overwrite target if it exists.
/// Breaking change: Default changed from false to true in version 0.40.0.
///
- bool Overwrite { get; init; }
+ bool Overwrite { get; set; }
///
/// Extract with internal directory structure.
/// Breaking change: Default changed from false to true in version 0.40.0.
///
- bool ExtractFullPath { get; init; }
+ bool ExtractFullPath { get; set; }
///
/// Preserve file time.
/// Breaking change: Default changed from false to true in version 0.40.0.
///
- bool PreserveFileTime { get; init; }
+ bool PreserveFileTime { get; set; }
///
/// Preserve windows file attributes.
///
- bool PreserveAttributes { get; init; }
+ bool PreserveAttributes { get; set; }
///
/// Delegate for writing symbolic links to disk.
/// The first parameter is the source path (where the symlink is created).
/// The second parameter is the target path (what the symlink refers to).
///
- Action? SymbolicLinkHandler { get; init; }
+ Action? SymbolicLinkHandler { get; set; }
}
diff --git a/src/SharpCompress/Common/Options/IProgressOptions.cs b/src/SharpCompress/Common/Options/IProgressOptions.cs
index 76bab1bf5..80e9dd16a 100644
--- a/src/SharpCompress/Common/Options/IProgressOptions.cs
+++ b/src/SharpCompress/Common/Options/IProgressOptions.cs
@@ -4,5 +4,5 @@ namespace SharpCompress.Common.Options;
public interface IProgressOptions
{
- IProgress? Progress { get; init; }
+ IProgress? Progress { get; set; }
}
diff --git a/src/SharpCompress/Common/Options/IReaderOptions.cs b/src/SharpCompress/Common/Options/IReaderOptions.cs
index 74d00de58..c25f48251 100644
--- a/src/SharpCompress/Common/Options/IReaderOptions.cs
+++ b/src/SharpCompress/Common/Options/IReaderOptions.cs
@@ -8,37 +8,37 @@ public interface IReaderOptions : IStreamOptions, IEncodingOptions, IProgressOpt
///
/// Look for RarArchive (Check for self-extracting archives or cases where RarArchive isn't at the start of the file)
///
- bool LookForHeader { get; init; }
+ bool LookForHeader { get; set; }
///
/// Password for encrypted archives.
///
- string? Password { get; init; }
+ string? Password { get; set; }
///
/// Disable checking for incomplete archives.
///
- bool DisableCheckIncomplete { get; init; }
+ bool DisableCheckIncomplete { get; set; }
///
/// Buffer size for stream operations.
///
- int BufferSize { get; init; }
+ int BufferSize { get; set; }
///
/// Provide a hint for the extension of the archive being read, can speed up finding the correct decoder.
///
- string? ExtensionHint { get; init; }
+ string? ExtensionHint { get; set; }
///
/// Size of the rewindable buffer for non-seekable streams.
///
- int? RewindableBufferSize { get; init; }
+ int? RewindableBufferSize { get; set; }
///
/// Registry of compression providers.
/// Defaults to but can be replaced with custom providers.
/// Use this to provide alternative decompression implementations.
///
- CompressionProviderRegistry Providers { get; init; }
+ CompressionProviderRegistry Providers { get; set; }
}
diff --git a/src/SharpCompress/Common/Options/IStreamOptions.cs b/src/SharpCompress/Common/Options/IStreamOptions.cs
index be0bbb9fe..94a607901 100644
--- a/src/SharpCompress/Common/Options/IStreamOptions.cs
+++ b/src/SharpCompress/Common/Options/IStreamOptions.cs
@@ -2,5 +2,5 @@ namespace SharpCompress.Common.Options;
public interface IStreamOptions
{
- bool LeaveStreamOpen { get; init; }
+ bool LeaveStreamOpen { get; set; }
}
diff --git a/src/SharpCompress/Common/Options/IWriterOptions.cs b/src/SharpCompress/Common/Options/IWriterOptions.cs
index 73dd1ec38..a7a1d719f 100644
--- a/src/SharpCompress/Common/Options/IWriterOptions.cs
+++ b/src/SharpCompress/Common/Options/IWriterOptions.cs
@@ -12,17 +12,17 @@ public interface IWriterOptions : IStreamOptions, IEncodingOptions, IProgressOpt
///
/// The compression type to use for the archive.
///
- CompressionType CompressionType { get; init; }
+ CompressionType CompressionType { get; set; }
///
/// The compression level to be used when the compression type supports variable levels.
///
- int CompressionLevel { get; init; }
+ int CompressionLevel { get; set; }
///
/// Registry of compression providers.
/// Defaults to but can be replaced with custom providers, such as
/// System.IO.Compression for Deflate/GZip on modern .NET.
///
- CompressionProviderRegistry Providers { get; init; }
+ CompressionProviderRegistry Providers { get; set; }
}
diff --git a/src/SharpCompress/Providers/CompressionContext.cs b/src/SharpCompress/Providers/CompressionContext.cs
index e0680646a..e74a7e9c7 100644
--- a/src/SharpCompress/Providers/CompressionContext.cs
+++ b/src/SharpCompress/Providers/CompressionContext.cs
@@ -12,22 +12,22 @@ public sealed record CompressionContext
///
/// The size of the input data, or -1 if unknown.
///
- public long InputSize { get; init; } = -1;
+ public long InputSize { get; set; } = -1;
///
/// The expected output size, or -1 if unknown.
///
- public long OutputSize { get; init; } = -1;
+ public long OutputSize { get; set; } = -1;
///
/// Properties bytes for the compression format (e.g., LZMA properties).
///
- public byte[]? Properties { get; init; }
+ public byte[]? Properties { get; set; }
///
/// Whether the underlying stream supports seeking.
///
- public bool CanSeek { get; init; }
+ public bool CanSeek { get; set; }
///
/// Additional format-specific options.
@@ -38,7 +38,7 @@ public sealed record CompressionContext
/// Examples of valid FormatOptions values include compression properties (e.g., LZMA properties),
/// format flags, or algorithm-specific configuration.
///
- public object? FormatOptions { get; init; }
+ public object? FormatOptions { get; set; }
///
/// Creates a CompressionContext from a stream.
@@ -51,7 +51,7 @@ public static CompressionContext FromStream(Stream stream) =>
///
/// Reader options for accessing archive metadata such as header encoding.
///
- public IReaderOptions? ReaderOptions { get; init; }
+ public IReaderOptions? ReaderOptions { get; set; }
///
/// Returns a new with the specified reader options.
diff --git a/src/SharpCompress/Readers/ReaderOptions.cs b/src/SharpCompress/Readers/ReaderOptions.cs
index 109e3e932..aced7169e 100644
--- a/src/SharpCompress/Readers/ReaderOptions.cs
+++ b/src/SharpCompress/Readers/ReaderOptions.cs
@@ -10,7 +10,7 @@ namespace SharpCompress.Readers;
/// Options for configuring reader behavior when opening archives.
///
///
-/// This class is immutable. Use preset properties and fluent helpers for common configurations:
+/// Use preset properties, setters, and fluent helpers for common configurations:
///
/// var options = ReaderOptions.ForExternalStream
/// .WithPassword("secret")
@@ -53,43 +53,43 @@ public sealed record ReaderOptions : IReaderOptions
///
///
///
- public bool LeaveStreamOpen { get; init; } = false;
+ public bool LeaveStreamOpen { get; set; } = false;
///
/// Encoding to use for archive entry names.
///
- public IArchiveEncoding ArchiveEncoding { get; init; } = new ArchiveEncoding();
+ public IArchiveEncoding ArchiveEncoding { get; set; } = new ArchiveEncoding();
///
/// Look for RarArchive (Check for self-extracting archives or cases where RarArchive isn't at the start of the file)
///
- public bool LookForHeader { get; init; }
+ public bool LookForHeader { get; set; }
///
/// Password for encrypted archives.
///
- public string? Password { get; init; }
+ public string? Password { get; set; }
///
/// Disable checking for incomplete archives.
///
- public bool DisableCheckIncomplete { get; init; }
+ public bool DisableCheckIncomplete { get; set; }
///
/// Buffer size for stream operations.
///
- public int BufferSize { get; init; } = Constants.BufferSize;
+ public int BufferSize { get; set; } = Constants.BufferSize;
///
/// Provide a hint for the extension of the archive being read, can speed up finding the correct decoder. Should be without the leading period in the form like: tar.gz or zip
///
- public string? ExtensionHint { get; init; }
+ public string? ExtensionHint { get; set; }
///
/// An optional progress reporter for tracking extraction operations.
/// When set, progress updates will be reported as entries are extracted.
///
- public IProgress? Progress { get; init; }
+ public IProgress? Progress { get; set; }
///
/// Size of the rewindable buffer for non-seekable streams.
@@ -133,14 +133,14 @@ public sealed record ReaderOptions : IReaderOptions
/// using var reader = ReaderFactory.OpenReader(networkStream, options);
///
///
- public int? RewindableBufferSize { get; init; }
+ public int? RewindableBufferSize { get; set; }
///
/// Registry of compression providers.
/// Defaults to but can be replaced with custom implementations, such as
/// System.IO.Compression for Deflate/GZip on modern .NET.
///
- public CompressionProviderRegistry Providers { get; init; } =
+ public CompressionProviderRegistry Providers { get; set; } =
CompressionProviderRegistry.Default;
///
diff --git a/src/SharpCompress/Writers/GZip/GZipWriterOptions.cs b/src/SharpCompress/Writers/GZip/GZipWriterOptions.cs
index b70b647a1..42da70e21 100644
--- a/src/SharpCompress/Writers/GZip/GZipWriterOptions.cs
+++ b/src/SharpCompress/Writers/GZip/GZipWriterOptions.cs
@@ -12,7 +12,7 @@ namespace SharpCompress.Writers.GZip;
/// Options for configuring GZip writer behavior.
///
///
-/// This class is immutable. Use factory methods for creation:
+/// Use factory methods, property setters, or fluent helpers for creation:
///
/// var options = WriterOptions.ForGZip().WithLeaveStreamOpen(false).WithCompressionLevel(9);
///
@@ -27,7 +27,7 @@ public sealed record GZipWriterOptions : IWriterOptions
public CompressionType CompressionType
{
get => CompressionType.GZip;
- init
+ set
{
if (value != CompressionType.GZip)
{
@@ -46,7 +46,7 @@ public CompressionType CompressionType
public int CompressionLevel
{
get => _compressionLevel;
- init
+ set
{
CompressionLevelValidation.Validate(CompressionType.GZip, value);
_compressionLevel = value;
@@ -56,24 +56,24 @@ public int CompressionLevel
///
/// SharpCompress will keep the supplied streams open. Default is true.
///
- public bool LeaveStreamOpen { get; init; } = true;
+ public bool LeaveStreamOpen { get; set; } = true;
///
/// Encoding to use for archive entry names.
///
- public IArchiveEncoding ArchiveEncoding { get; init; } = new ArchiveEncoding();
+ public IArchiveEncoding ArchiveEncoding { get; set; } = new ArchiveEncoding();
///
/// An optional progress reporter for tracking compression operations.
///
- public IProgress? Progress { get; init; }
+ public IProgress? Progress { get; set; }
///
/// Registry of compression providers.
/// Defaults to but can be replaced with custom implementations, such as
/// System.IO.Compression for GZip on modern .NET.
///
- public CompressionProviderRegistry Providers { get; init; } =
+ public CompressionProviderRegistry Providers { get; set; } =
CompressionProviderRegistry.Default;
///
diff --git a/src/SharpCompress/Writers/SevenZip/SevenZipWriterOptions.cs b/src/SharpCompress/Writers/SevenZip/SevenZipWriterOptions.cs
index 033f07fdd..86fa09ffb 100644
--- a/src/SharpCompress/Writers/SevenZip/SevenZipWriterOptions.cs
+++ b/src/SharpCompress/Writers/SevenZip/SevenZipWriterOptions.cs
@@ -20,7 +20,7 @@ public sealed record SevenZipWriterOptions : IWriterOptions
public CompressionType CompressionType
{
get => _compressionType;
- init
+ set
{
if (value != CompressionType.LZMA && value != CompressionType.LZMA2)
{
@@ -39,41 +39,41 @@ public CompressionType CompressionType
public int CompressionLevel
{
get => _compressionLevel;
- init => _compressionLevel = value;
+ set => _compressionLevel = value;
}
///
/// SharpCompress will keep the supplied streams open. Default is true.
///
- public bool LeaveStreamOpen { get; init; } = true;
+ public bool LeaveStreamOpen { get; set; } = true;
///
/// Encoding to use for archive entry names.
///
- public IArchiveEncoding ArchiveEncoding { get; init; } = new ArchiveEncoding();
+ public IArchiveEncoding ArchiveEncoding { get; set; } = new ArchiveEncoding();
///
/// An optional progress reporter for tracking compression operations.
///
- public IProgress? Progress { get; init; }
+ public IProgress? Progress { get; set; }
///
/// Registry of compression providers.
/// Defaults to but can be replaced with custom implementations.
///
- public CompressionProviderRegistry Providers { get; init; } =
+ public CompressionProviderRegistry Providers { get; set; } =
CompressionProviderRegistry.Default;
///
/// Whether to compress the archive header itself using LZMA.
/// Default is true, matching standard 7-Zip behavior.
///
- public bool CompressHeader { get; init; } = true;
+ public bool CompressHeader { get; set; } = true;
///
/// Custom LZMA encoder properties. Null uses defaults (1MB dictionary, 32 fast bytes).
///
- public LzmaEncoderProperties? LzmaProperties { get; init; }
+ public LzmaEncoderProperties? LzmaProperties { get; set; }
///
/// Creates a new SevenZipWriterOptions instance with LZMA2 compression (default).
diff --git a/src/SharpCompress/Writers/Tar/TarWriterOptions.cs b/src/SharpCompress/Writers/Tar/TarWriterOptions.cs
index 16e5df9f9..004d46430 100755
--- a/src/SharpCompress/Writers/Tar/TarWriterOptions.cs
+++ b/src/SharpCompress/Writers/Tar/TarWriterOptions.cs
@@ -11,7 +11,7 @@ namespace SharpCompress.Writers.Tar;
/// Options for configuring Tar writer behavior.
///
///
-/// This class is immutable. Use the with expression to create modified copies:
+/// Configure tar writing with constructors, property setters, or the with expression:
///
/// var options = new TarWriterOptions(CompressionType.GZip, true);
/// options = options with { HeaderFormat = TarHeaderWriteFormat.V7 };
@@ -22,45 +22,44 @@ public sealed record TarWriterOptions : IWriterOptions
///
/// The compression type to use for the archive.
///
- public CompressionType CompressionType { get; init; }
+ public CompressionType CompressionType { get; set; }
///
/// The compression level to be used when the compression type supports variable levels.
///
- public int CompressionLevel { get; init; }
+ public int CompressionLevel { get; set; }
///
/// SharpCompress will keep the supplied streams open. Default is true.
///
- public bool LeaveStreamOpen { get; init; } = true;
+ public bool LeaveStreamOpen { get; set; } = true;
///
/// Encoding to use for archive entry names.
///
- public IArchiveEncoding ArchiveEncoding { get; init; } = new ArchiveEncoding();
+ public IArchiveEncoding ArchiveEncoding { get; set; } = new ArchiveEncoding();
///
/// An optional progress reporter for tracking compression operations.
///
- public IProgress? Progress { get; init; }
+ public IProgress? Progress { get; set; }
///
/// Registry of compression providers.
/// Defaults to but can be replaced with custom implementations.
///
- public CompressionProviderRegistry Providers { get; init; } =
+ public CompressionProviderRegistry Providers { get; set; } =
CompressionProviderRegistry.Default;
///
/// Indicates if archive should be finalized (by 2 empty blocks) on close.
///
- public bool FinalizeArchiveOnClose { get; init; } = true;
+ public bool FinalizeArchiveOnClose { get; set; } = true;
///
/// The format to use when writing tar headers.
///
- public TarHeaderWriteFormat HeaderFormat { get; init; } =
- TarHeaderWriteFormat.GNU_TAR_LONG_LINK;
+ public TarHeaderWriteFormat HeaderFormat { get; set; } = TarHeaderWriteFormat.GNU_TAR_LONG_LINK;
///
/// Creates a new TarWriterOptions instance with the specified compression type and finalization option.
diff --git a/src/SharpCompress/Writers/WriterOptions.cs b/src/SharpCompress/Writers/WriterOptions.cs
index 6415ca3e0..e4dd4ac06 100644
--- a/src/SharpCompress/Writers/WriterOptions.cs
+++ b/src/SharpCompress/Writers/WriterOptions.cs
@@ -10,7 +10,7 @@ namespace SharpCompress.Writers;
/// Options for configuring writer behavior when creating archives.
///
///
-/// This class is immutable. Use factory methods for creation:
+/// Use factory methods, property setters, or fluent helpers for creation:
///
/// var options = WriterOptions.ForZip().WithLeaveStreamOpen(false).WithCompressionLevel(9);
///
@@ -20,7 +20,7 @@ public sealed record WriterOptions : IWriterOptions
///
/// The compression type to use for the archive.
///
- public CompressionType CompressionType { get; init; }
+ public CompressionType CompressionType { get; set; }
///
/// The compression level to be used when the compression type supports variable levels.
@@ -33,7 +33,7 @@ public sealed record WriterOptions : IWriterOptions
public int CompressionLevel
{
get;
- init
+ set
{
CompressionLevelValidation.Validate(CompressionType, value);
field = value;
@@ -43,25 +43,25 @@ public int CompressionLevel
///
/// SharpCompress will keep the supplied streams open. Default is true.
///
- public bool LeaveStreamOpen { get; init; } = true;
+ public bool LeaveStreamOpen { get; set; } = true;
///
/// Encoding to use for archive entry names.
///
- public IArchiveEncoding ArchiveEncoding { get; init; } = new ArchiveEncoding();
+ public IArchiveEncoding ArchiveEncoding { get; set; } = new ArchiveEncoding();
///
/// An optional progress reporter for tracking compression operations.
/// When set, progress updates will be reported as entries are written.
///
- public IProgress? Progress { get; init; }
+ public IProgress? Progress { get; set; }
///
/// Registry of compression providers.
/// Defaults to but can be replaced with custom implementations, such as
/// System.IO.Compression for Deflate/GZip on modern .NET.
///
- public CompressionProviderRegistry Providers { get; init; } =
+ public CompressionProviderRegistry Providers { get; set; } =
CompressionProviderRegistry.Default;
///
diff --git a/src/SharpCompress/Writers/Zip/ZipWriterOptions.cs b/src/SharpCompress/Writers/Zip/ZipWriterOptions.cs
index 0089d7c82..e3bf28343 100644
--- a/src/SharpCompress/Writers/Zip/ZipWriterOptions.cs
+++ b/src/SharpCompress/Writers/Zip/ZipWriterOptions.cs
@@ -13,7 +13,7 @@ namespace SharpCompress.Writers.Zip;
/// Options for configuring Zip writer behavior.
///
///
-/// This class is immutable. Use the with expression to create modified copies:
+/// Configure zip writing with constructors, property setters, or the with expression:
///
/// var options = new ZipWriterOptions(CompressionType.Zip);
/// options = options with { UseZip64 = true };
@@ -30,7 +30,7 @@ public sealed record ZipWriterOptions : IWriterOptions
public CompressionType CompressionType
{
get => _compressionType;
- init => _compressionType = value;
+ set => _compressionType = value;
}
///
@@ -39,7 +39,7 @@ public CompressionType CompressionType
public int CompressionLevel
{
get => _compressionLevel;
- init
+ set
{
CompressionLevelValidation.Validate(CompressionType, value);
_compressionLevel = value;
@@ -49,29 +49,29 @@ public int CompressionLevel
///
/// SharpCompress will keep the supplied streams open. Default is true.
///
- public bool LeaveStreamOpen { get; init; } = true;
+ public bool LeaveStreamOpen { get; set; } = true;
///
/// Encoding to use for archive entry names.
///
- public IArchiveEncoding ArchiveEncoding { get; init; } = new ArchiveEncoding();
+ public IArchiveEncoding ArchiveEncoding { get; set; } = new ArchiveEncoding();
///
/// An optional progress reporter for tracking compression operations.
///
- public IProgress? Progress { get; init; }
+ public IProgress? Progress { get; set; }
///
/// Registry of compression providers.
/// Defaults to but can be replaced with custom implementations.
///
- public CompressionProviderRegistry Providers { get; init; } =
+ public CompressionProviderRegistry Providers { get; set; } =
CompressionProviderRegistry.Default;
///
/// Optional comment for the archive.
///
- public string? ArchiveComment { get; init; }
+ public string? ArchiveComment { get; set; }
///
/// Sets a value indicating if zip64 support is enabled.
@@ -80,7 +80,7 @@ public int CompressionLevel
/// Archives larger than 4GiB are supported as long as all streams
/// are less than 4GiB in length.
///
- public bool UseZip64 { get; init; }
+ public bool UseZip64 { get; set; }
///
/// Creates a new ZipWriterOptions instance with the specified compression type.
diff --git a/src/SharpCompress/packages.lock.json b/src/SharpCompress/packages.lock.json
index f522434d0..965d59a8d 100644
--- a/src/SharpCompress/packages.lock.json
+++ b/src/SharpCompress/packages.lock.json
@@ -36,6 +36,12 @@
"resolved": "17.14.15",
"contentHash": "mXQPJsbuUD2ydq4/ffd8h8tSOFCXec+2xJOVNCvXjuMOq/+5EKHq3D2m2MC2+nUaXeFMSt66VS/J4HdKBixgcw=="
},
+ "PolySharp": {
+ "type": "Direct",
+ "requested": "[1.15.0, )",
+ "resolved": "1.15.0",
+ "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g=="
+ },
"System.Text.Encoding.CodePages": {
"type": "Direct",
"requested": "[8.0.0, )",
@@ -139,6 +145,12 @@
"Microsoft.NETCore.Platforms": "1.1.0"
}
},
+ "PolySharp": {
+ "type": "Direct",
+ "requested": "[1.15.0, )",
+ "resolved": "1.15.0",
+ "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g=="
+ },
"System.Text.Encoding.CodePages": {
"type": "Direct",
"requested": "[8.0.0, )",
@@ -235,6 +247,12 @@
"resolved": "17.14.15",
"contentHash": "mXQPJsbuUD2ydq4/ffd8h8tSOFCXec+2xJOVNCvXjuMOq/+5EKHq3D2m2MC2+nUaXeFMSt66VS/J4HdKBixgcw=="
},
+ "PolySharp": {
+ "type": "Direct",
+ "requested": "[1.15.0, )",
+ "resolved": "1.15.0",
+ "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g=="
+ },
"System.Text.Encoding.CodePages": {
"type": "Direct",
"requested": "[8.0.0, )",
@@ -268,9 +286,9 @@
"net10.0": {
"Microsoft.NET.ILLink.Tasks": {
"type": "Direct",
- "requested": "[10.0.6, )",
- "resolved": "10.0.6",
- "contentHash": "QKuvS0LWX4fjFqeDkyM7Kqt8P3wYTiPD4nwU+9y59n0sCiG714fxDgbbN82vDnzq89AF/PiHl92TP2C4aFDUQA=="
+ "requested": "[10.0.7, )",
+ "resolved": "10.0.7",
+ "contentHash": "AA/yhzFHNtQZXLdqjzujPy25G8EWwGWsAnxOE2zYSBoT/8QHP6ketN3CToD3DFreO653ipUwnKHo22B8AlBMCw=="
},
"Microsoft.NETFramework.ReferenceAssemblies": {
"type": "Direct",
@@ -297,6 +315,12 @@
"resolved": "17.14.15",
"contentHash": "mXQPJsbuUD2ydq4/ffd8h8tSOFCXec+2xJOVNCvXjuMOq/+5EKHq3D2m2MC2+nUaXeFMSt66VS/J4HdKBixgcw=="
},
+ "PolySharp": {
+ "type": "Direct",
+ "requested": "[1.15.0, )",
+ "resolved": "1.15.0",
+ "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g=="
+ },
"Microsoft.Build.Tasks.Git": {
"type": "Transitive",
"resolved": "10.0.102",
@@ -339,6 +363,12 @@
"resolved": "17.14.15",
"contentHash": "mXQPJsbuUD2ydq4/ffd8h8tSOFCXec+2xJOVNCvXjuMOq/+5EKHq3D2m2MC2+nUaXeFMSt66VS/J4HdKBixgcw=="
},
+ "PolySharp": {
+ "type": "Direct",
+ "requested": "[1.15.0, )",
+ "resolved": "1.15.0",
+ "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g=="
+ },
"Microsoft.Build.Tasks.Git": {
"type": "Transitive",
"resolved": "10.0.102",
@@ -387,6 +417,12 @@
"resolved": "17.14.15",
"contentHash": "mXQPJsbuUD2ydq4/ffd8h8tSOFCXec+2xJOVNCvXjuMOq/+5EKHq3D2m2MC2+nUaXeFMSt66VS/J4HdKBixgcw=="
},
+ "PolySharp": {
+ "type": "Direct",
+ "requested": "[1.15.0, )",
+ "resolved": "1.15.0",
+ "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g=="
+ },
"Microsoft.Build.Tasks.Git": {
"type": "Transitive",
"resolved": "10.0.102",
diff --git a/tests/SharpCompress.Performance/packages.lock.json b/tests/SharpCompress.Performance/packages.lock.json
index 939ede421..35d18d202 100644
--- a/tests/SharpCompress.Performance/packages.lock.json
+++ b/tests/SharpCompress.Performance/packages.lock.json
@@ -55,6 +55,12 @@
"resolved": "17.14.15",
"contentHash": "mXQPJsbuUD2ydq4/ffd8h8tSOFCXec+2xJOVNCvXjuMOq/+5EKHq3D2m2MC2+nUaXeFMSt66VS/J4HdKBixgcw=="
},
+ "PolySharp": {
+ "type": "Direct",
+ "requested": "[1.15.0, )",
+ "resolved": "1.15.0",
+ "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g=="
+ },
"BenchmarkDotNet.Annotations": {
"type": "Transitive",
"resolved": "0.15.8",
diff --git a/tests/SharpCompress.Test/OptionsUsabilityTests.cs b/tests/SharpCompress.Test/OptionsUsabilityTests.cs
index f9f05aed5..11376f755 100644
--- a/tests/SharpCompress.Test/OptionsUsabilityTests.cs
+++ b/tests/SharpCompress.Test/OptionsUsabilityTests.cs
@@ -1,5 +1,8 @@
using System;
using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using SharpCompress.Archives;
using SharpCompress.Common;
@@ -228,6 +231,65 @@ public void ExtractionOptions_Presets_Have_Correct_Defaults()
Assert.True(preserveMetadata.PreserveAttributes);
}
+ [Fact]
+ public void Public_Api_Does_Not_Expose_CSharp_9_Required_Metadata()
+ {
+ var assembly = typeof(ReaderOptions).Assembly;
+ const string RequiredMemberAttributeName =
+ "System.Runtime.CompilerServices.RequiredMemberAttribute";
+ const string SetsRequiredMembersAttributeName =
+ "System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute";
+ var initOnlyProperties = assembly
+ .GetExportedTypes()
+ .SelectMany(type =>
+ type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
+ .Where(property => property.SetMethod?.IsPublic == true)
+ .Where(property =>
+ property
+ .SetMethod!.ReturnParameter.GetRequiredCustomModifiers()
+ .Contains(typeof(IsExternalInit))
+ )
+ .Select(property => $"{type.FullName}.{property.Name}")
+ )
+ .ToArray();
+
+ Assert.Empty(initOnlyProperties);
+
+ var requiredMembers = assembly
+ .GetExportedTypes()
+ .SelectMany(type =>
+ type.GetMembers(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public)
+ .Where(member =>
+ member
+ .GetCustomAttributesData()
+ .Any(attribute =>
+ attribute.AttributeType.FullName == RequiredMemberAttributeName
+ )
+ )
+ .Select(member => $"{type.FullName}.{member.Name}")
+ )
+ .ToArray();
+
+ Assert.Empty(requiredMembers);
+
+ var constructorsWithRequiredMembers = assembly
+ .GetExportedTypes()
+ .SelectMany(type =>
+ type.GetConstructors(BindingFlags.Instance | BindingFlags.Public)
+ .Where(constructor =>
+ constructor
+ .GetCustomAttributesData()
+ .Any(attribute =>
+ attribute.AttributeType.FullName == SetsRequiredMembersAttributeName
+ )
+ )
+ .Select(_ => $"{type.FullName}.ctor")
+ )
+ .ToArray();
+
+ Assert.Empty(constructorsWithRequiredMembers);
+ }
+
[Fact]
public void ReaderOptions_Factory_ForEncryptedArchive_Sets_Password()
{
diff --git a/tests/SharpCompress.Test/packages.lock.json b/tests/SharpCompress.Test/packages.lock.json
index 9a2199223..136bb015c 100644
--- a/tests/SharpCompress.Test/packages.lock.json
+++ b/tests/SharpCompress.Test/packages.lock.json
@@ -45,6 +45,12 @@
"resolved": "17.14.15",
"contentHash": "mXQPJsbuUD2ydq4/ffd8h8tSOFCXec+2xJOVNCvXjuMOq/+5EKHq3D2m2MC2+nUaXeFMSt66VS/J4HdKBixgcw=="
},
+ "PolySharp": {
+ "type": "Direct",
+ "requested": "[1.15.0, )",
+ "resolved": "1.15.0",
+ "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g=="
+ },
"xunit.runner.visualstudio": {
"type": "Direct",
"requested": "[3.1.5, )",
@@ -309,6 +315,30 @@
}
}
},
+ ".NETFramework,Version=v4.8/win-x86": {
+ "Microsoft.Win32.Registry": {
+ "type": "Transitive",
+ "resolved": "5.0.0",
+ "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==",
+ "dependencies": {
+ "System.Security.AccessControl": "5.0.0",
+ "System.Security.Principal.Windows": "5.0.0"
+ }
+ },
+ "System.Security.AccessControl": {
+ "type": "Transitive",
+ "resolved": "5.0.0",
+ "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==",
+ "dependencies": {
+ "System.Security.Principal.Windows": "5.0.0"
+ }
+ },
+ "System.Security.Principal.Windows": {
+ "type": "Transitive",
+ "resolved": "5.0.0",
+ "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA=="
+ }
+ },
"net10.0": {
"AwesomeAssertions": {
"type": "Direct",
@@ -351,6 +381,12 @@
"resolved": "17.14.15",
"contentHash": "mXQPJsbuUD2ydq4/ffd8h8tSOFCXec+2xJOVNCvXjuMOq/+5EKHq3D2m2MC2+nUaXeFMSt66VS/J4HdKBixgcw=="
},
+ "PolySharp": {
+ "type": "Direct",
+ "requested": "[1.15.0, )",
+ "resolved": "1.15.0",
+ "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g=="
+ },
"xunit.runner.visualstudio": {
"type": "Direct",
"requested": "[3.1.5, )",
@@ -521,6 +557,13 @@
"resolved": "8.0.0",
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw=="
}
+ },
+ "net10.0/win-x86": {
+ "Microsoft.Win32.Registry": {
+ "type": "Transitive",
+ "resolved": "5.0.0",
+ "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg=="
+ }
}
}
}
\ No newline at end of file