diff --git a/Source/.editorconfig b/.editorconfig
similarity index 83%
rename from Source/.editorconfig
rename to .editorconfig
index 1243003..0a1bbce 100644
--- a/Source/.editorconfig
+++ b/.editorconfig
@@ -9,12 +9,21 @@ root = true
guidelines = 160
-########### C# Project files ###########
+########### Project XML files ###########
[*.csproj]
-#### Core EditorConfig Options ####
+indent_size = 2
+indent_style = space
+tab_width = 2
+
+[*.props]
+
+indent_size = 2
+indent_style = space
+tab_width = 2
+
+[*.targets]
-# Indentation and spacing
indent_size = 2
indent_style = space
tab_width = 2
@@ -81,10 +90,13 @@ dotnet_code_quality_unused_parameters = all:suggestion
#### C# Coding Conventions ####
+# Namespace preferences
+csharp_style_namespace_declarations = file_scoped:warning
+
# var preferences
csharp_style_var_elsewhere = false:silent
-csharp_style_var_for_built_in_types = false:suggestion
-csharp_style_var_when_type_is_apparent = true:suggestion
+csharp_style_var_for_built_in_types = false:warning
+csharp_style_var_when_type_is_apparent = true:warning
# Expression-bodied members
csharp_style_expression_bodied_accessors = true:suggestion
@@ -184,6 +196,14 @@ dotnet_naming_rule.types_should_be_pascalcase.severity = warning
dotnet_naming_rule.types_should_be_pascalcase.symbols = types
dotnet_naming_rule.types_should_be_pascalcase.style = pascalcase
+dotnet_naming_rule.private_static_field_should_be_s_prefixed_camelcase.severity = none
+dotnet_naming_rule.private_static_field_should_be_s_prefixed_camelcase.symbols = private_static_field
+dotnet_naming_rule.private_static_field_should_be_s_prefixed_camelcase.style = prefixed_camelcase
+
+dotnet_naming_rule.private_field_should_be_prefixed_camelcase.severity = warning
+dotnet_naming_rule.private_field_should_be_prefixed_camelcase.symbols = private_field
+dotnet_naming_rule.private_field_should_be_prefixed_camelcase.style = prefixed_camelcase
+
dotnet_naming_rule.constant_field_should_be_pascalcase.severity = warning
dotnet_naming_rule.constant_field_should_be_pascalcase.symbols = constant_field
dotnet_naming_rule.constant_field_should_be_pascalcase.style = pascalcase
@@ -209,12 +229,25 @@ dotnet_naming_symbols.constant_field.applicable_kinds = field
dotnet_naming_symbols.constant_field.applicable_accessibilities = public, internal, private, protected, protected_internal
dotnet_naming_symbols.constant_field.required_modifiers = const
+dotnet_naming_symbols.private_static_field.applicable_kinds = field
+dotnet_naming_symbols.private_static_field.applicable_accessibilities = private
+dotnet_naming_symbols.private_static_field.required_modifiers = static
+
+dotnet_naming_symbols.private_field.applicable_kinds = field
+dotnet_naming_symbols.private_field.applicable_accessibilities = private
+dotnet_naming_symbols.private_field.required_modifiers =
+
# Naming styles
dotnet_naming_style.pascalcase.required_prefix =
dotnet_naming_style.pascalcase.required_suffix =
dotnet_naming_style.pascalcase.word_separator =
dotnet_naming_style.pascalcase.capitalization = pascal_case
+dotnet_naming_style.prefixed_camelcase.required_prefix = _
+dotnet_naming_style.prefixed_camelcase.required_suffix =
+dotnet_naming_style.prefixed_camelcase.word_separator =
+dotnet_naming_style.prefixed_camelcase.capitalization = camel_case
+
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
@@ -346,3 +379,21 @@ dotnet_diagnostic.SA1519.severity = silent
# SA1214: Readonly fields should appear before non-readonly fields
dotnet_diagnostic.SA1214.severity = suggestion
+
+# SA1518: Use line endings correctly at end of file
+dotnet_diagnostic.SA1518.severity = none
+
+# SA1402: File may only contain a single type
+dotnet_diagnostic.SA1402.severity = none
+
+# IDE0028: Simplify collection initialization
+dotnet_diagnostic.IDE0028.severity = warning
+
+# IDE0057: Use range operator
+dotnet_diagnostic.IDE0057.severity = warning
+
+# IDE0059: Unnecessary assignment of a value
+dotnet_diagnostic.IDE0059.severity = warning
+
+# RS0030: Do not use banned APIs
+dotnet_diagnostic.RS0030.severity = error
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 46500d8..790d362 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -2,11 +2,11 @@ version: 2
updates:
- package-ecosystem: nuget
target-branch: main
- directory: "/Source"
+ directory: "/"
schedule:
interval: weekly
- package-ecosystem: "github-actions"
directory: "/"
- schedule:
+ schedule:
interval: weekly
target-branch: main
\ No newline at end of file
diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml
index 4d210c3..b48a9b9 100644
--- a/.github/workflows/build-and-test.yml
+++ b/.github/workflows/build-and-test.yml
@@ -19,18 +19,15 @@ jobs:
dotnet-version: |
6.0.x
7.0.x
+ 8.0.x
- name: Clean
run: dotnet clean --configuration Debug && dotnet nuget locals all --clear
- working-directory: Source
- name: Install dependencies
run: dotnet restore
- working-directory: Source
- name: Build
run: dotnet build --configuration Debug --no-restore
- working-directory: Source
- name: Test
run: dotnet test --configuration Debug --no-build --verbosity normal
- working-directory: Source
release-windows:
@@ -44,15 +41,12 @@ jobs:
dotnet-version: |
6.0.x
7.0.x
+ 8.0.x
- name: Clean
run: dotnet clean --configuration Release && dotnet nuget locals all --clear
- working-directory: Source
- name: Install dependencies
run: dotnet restore
- working-directory: Source
- name: Build
run: dotnet build --configuration Release --no-restore
- working-directory: Source
- name: Test
- run: dotnet test --configuration Release --no-build --verbosity normal
- working-directory: Source
\ No newline at end of file
+ run: dotnet test --configuration Release --no-build --verbosity normal
\ No newline at end of file
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 0000000..3d7cee5
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,30 @@
+
+
+ 12.0
+ enable
+ annotations
+ CS8769
+ enable
+ true
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/LICENSE.md b/LICENSE.md
index bf321cb..ab642f8 100644
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -1,7 +1,7 @@
The MIT License (MIT)
-Copyright (c) 2023
+Copyright (c) 2023 Singulink
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/Singulink.Collections.sln b/Singulink.Collections.sln
new file mode 100644
index 0000000..efc4ed5
--- /dev/null
+++ b/Singulink.Collections.sln
@@ -0,0 +1,101 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Singulink.Collections", "Source\Singulink.Collections\Singulink.Collections.csproj", "{1078945F-D4EB-45D7-BD1F-EF33D7A7D308}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Singulink.Collections.Weak", "Source\Singulink.Collections.Weak\Singulink.Collections.Weak.csproj", "{D13CB713-78B9-422B-9BAF-2A35B434FA42}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Singulink.Collections.Tests", "Tests\Singulink.Collections.Tests\Singulink.Collections.Tests.csproj", "{221DC5F0-312A-4FB8-B2FC-D171C921CED6}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Singulink.Collections.Weak.Tests", "Tests\Singulink.Collections.Weak.Tests\Singulink.Collections.Weak.Tests.csproj", "{2F4F3F7C-86C3-473D-90D1-9EFC7147B8D8}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{3F3E66F3-E5F4-4243-AB36-583AC6BDDB22}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{D641285C-EB50-4B2E-9FCB-6650DAA65342}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{77D5520C-C625-44CF-9D13-94563173C295}"
+ ProjectSection(SolutionItems) = preProject
+ .editorconfig = .editorconfig
+ CHANGELOG.md = CHANGELOG.md
+ Directory.Build.props = Directory.Build.props
+ LICENSE.md = LICENSE.md
+ README.md = README.md
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{13264A11-5846-4A14-8E15-0A54AA04BEAB}"
+ ProjectSection(SolutionItems) = preProject
+ Docs\docfx.json = Docs\docfx.json
+ Docs\index.md = Docs\index.md
+ Docs\toc.yml = Docs\toc.yml
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "api", "api", "{85134FD9-55CA-477B-A8E9-E3F8A6B323F1}"
+ ProjectSection(SolutionItems) = preProject
+ Docs\api\index.md = Docs\api\index.md
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{4C819D2C-90B2-4490-B5B0-53F1DD9D9EE1}"
+ ProjectSection(SolutionItems) = preProject
+ .github\dependabot.yml = .github\dependabot.yml
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source Items", "Source Items", "{E264B84B-C48C-4D27-8FD2-8C2DE0BAB9B2}"
+ ProjectSection(SolutionItems) = preProject
+ Source\Directory.Build.props = Source\Directory.Build.props
+ Source\Directory.Build.targets = Source\Directory.Build.targets
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test Items", "Test Items", "{3C98511D-3E04-4D16-B96C-8EFC5216CF4E}"
+ ProjectSection(SolutionItems) = preProject
+ Tests\.editorconfig = Tests\.editorconfig
+ Tests\BannedSymbols.txt = Tests\BannedSymbols.txt
+ Tests\Directory.Build.props = Tests\Directory.Build.props
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{6371A96E-144C-47C4-BB04-8BC4E6ADA28F}"
+ ProjectSection(SolutionItems) = preProject
+ .github\workflows\build-and-test.yml = .github\workflows\build-and-test.yml
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {1078945F-D4EB-45D7-BD1F-EF33D7A7D308}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1078945F-D4EB-45D7-BD1F-EF33D7A7D308}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1078945F-D4EB-45D7-BD1F-EF33D7A7D308}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1078945F-D4EB-45D7-BD1F-EF33D7A7D308}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D13CB713-78B9-422B-9BAF-2A35B434FA42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D13CB713-78B9-422B-9BAF-2A35B434FA42}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D13CB713-78B9-422B-9BAF-2A35B434FA42}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D13CB713-78B9-422B-9BAF-2A35B434FA42}.Release|Any CPU.Build.0 = Release|Any CPU
+ {221DC5F0-312A-4FB8-B2FC-D171C921CED6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {221DC5F0-312A-4FB8-B2FC-D171C921CED6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {221DC5F0-312A-4FB8-B2FC-D171C921CED6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {221DC5F0-312A-4FB8-B2FC-D171C921CED6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2F4F3F7C-86C3-473D-90D1-9EFC7147B8D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2F4F3F7C-86C3-473D-90D1-9EFC7147B8D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2F4F3F7C-86C3-473D-90D1-9EFC7147B8D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2F4F3F7C-86C3-473D-90D1-9EFC7147B8D8}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {1078945F-D4EB-45D7-BD1F-EF33D7A7D308} = {3F3E66F3-E5F4-4243-AB36-583AC6BDDB22}
+ {D13CB713-78B9-422B-9BAF-2A35B434FA42} = {3F3E66F3-E5F4-4243-AB36-583AC6BDDB22}
+ {221DC5F0-312A-4FB8-B2FC-D171C921CED6} = {D641285C-EB50-4B2E-9FCB-6650DAA65342}
+ {2F4F3F7C-86C3-473D-90D1-9EFC7147B8D8} = {D641285C-EB50-4B2E-9FCB-6650DAA65342}
+ {85134FD9-55CA-477B-A8E9-E3F8A6B323F1} = {13264A11-5846-4A14-8E15-0A54AA04BEAB}
+ {E264B84B-C48C-4D27-8FD2-8C2DE0BAB9B2} = {3F3E66F3-E5F4-4243-AB36-583AC6BDDB22}
+ {3C98511D-3E04-4D16-B96C-8EFC5216CF4E} = {D641285C-EB50-4B2E-9FCB-6650DAA65342}
+ {6371A96E-144C-47C4-BB04-8BC4E6ADA28F} = {4C819D2C-90B2-4490-B5B0-53F1DD9D9EE1}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {A726AC5A-8E85-4678-ABD0-6746252A4C38}
+ EndGlobalSection
+EndGlobal
diff --git a/Source/Directory.Build.props b/Source/Directory.Build.props
index 2f115fe..4506251 100644
--- a/Source/Directory.Build.props
+++ b/Source/Directory.Build.props
@@ -1,31 +1,24 @@
-
- 11.0
- enable
- enable
- 6.0.0
- true
-
+
-
- true
-
+
+ Singulink
+ MIT
+ © Singulink. All rights reserved.
+ Singulink Icon 128x128.png
+
-
-
-
+
+ true
+ true
+ true
+ true
+
-
-
-
-
-
-
-
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
+
+
+ True
+
+
+
\ No newline at end of file
diff --git a/Source/Directory.Build.targets b/Source/Directory.Build.targets
index 6d4f10f..a41ecce 100644
--- a/Source/Directory.Build.targets
+++ b/Source/Directory.Build.targets
@@ -1,34 +1,34 @@
-
- $(BeforePack);AddCompatWarningsToPackage
-
+
+ $(BeforePack);AddCompatWarningsToPackage
+
-
-
- <_CompatWarningFilePath>$(BaseIntermediateOutputPath)CompatWarning.targets
- <_CompatWarningTarget>SLCompatWarning_$(PackageId.Replace('.', '_'))
- <_CompatWarningFileContent>
-
+
+
+ <_CompatWarningFilePath>$(BaseIntermediateOutputPath)CompatWarning.targets
+ <_CompatWarningTarget>SLCompatWarning_$(PackageId.Replace('.', '_'))
+ <_CompatWarningFileContent>
+
]]>
-
-
+
+
-
+
-
-
+
+
-
-
-
-
+
+
+
+
\ No newline at end of file
diff --git a/Source/Singulink.Collections.Abstractions/FodyWeavers.xml b/Source/Singulink.Collections.Abstractions/FodyWeavers.xml
deleted file mode 100644
index a848a9d..0000000
--- a/Source/Singulink.Collections.Abstractions/FodyWeavers.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/Source/Singulink.Collections.Abstractions/FodyWeavers.xsd b/Source/Singulink.Collections.Abstractions/FodyWeavers.xsd
deleted file mode 100644
index 0ceaa88..0000000
--- a/Source/Singulink.Collections.Abstractions/FodyWeavers.xsd
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-
-
-
-
-
-
-
- Determines whether outputs (return values and out/ref parameters) have null checks injected.
-
-
-
-
- Determines whether non-public members have null checks injected or if only public entry points are checked.
-
-
-
-
-
-
-
- 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
-
-
-
-
- A comma-separated list of error codes that can be safely ignored in assembly verification.
-
-
-
-
- 'false' to turn off automatic generation of the XML Schema file.
-
-
-
-
-
\ No newline at end of file
diff --git a/Source/Singulink.Collections.Abstractions/Singulink.Collections.Abstractions.csproj b/Source/Singulink.Collections.Abstractions/Singulink.Collections.Abstractions.csproj
deleted file mode 100644
index 0144e73..0000000
--- a/Source/Singulink.Collections.Abstractions/Singulink.Collections.Abstractions.csproj
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
- netstandard2.0;net6.0
- Singulink.Collections
- 2.0
- Singulink
- MIT
- © Singulink. All rights reserved.
- https://github.com/Singulink/Singulink.Collections
- Singulink Icon 128x128.png
-
- Collection abstractions for Singulink.Collections.
-
- Commonly Used Types:
- System.Collections.Generic.IReadOnlySet<T> (netstandard polyfill)
- Singulink.Collections.ICollectionDictionary<TKey, TValue>
- Singulink.Collections.IReadOnlyCollectionDictionary<TKey, TValue>
- Singulink.Collections.IMap<TLeft, TRight>
- Singulink.Collections.IReadOnlyMap<TLeft, TRight>
-
- true
- key.snk
- true
- true
-
-
-
- true
- true
- true
- true
-
-
-
-
-
-
-
-
- True
-
-
-
-
\ No newline at end of file
diff --git a/Source/Singulink.Collections.Abstractions/key.snk b/Source/Singulink.Collections.Abstractions/key.snk
deleted file mode 100644
index befc05f..0000000
Binary files a/Source/Singulink.Collections.Abstractions/key.snk and /dev/null differ
diff --git a/Source/Singulink.Collections.Weak/FodyWeavers.xml b/Source/Singulink.Collections.Weak/FodyWeavers.xml
deleted file mode 100644
index a848a9d..0000000
--- a/Source/Singulink.Collections.Weak/FodyWeavers.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/Source/Singulink.Collections.Weak/Singulink.Collections.Weak.csproj b/Source/Singulink.Collections.Weak/Singulink.Collections.Weak.csproj
index 2d0a6f4..5265463 100644
--- a/Source/Singulink.Collections.Weak/Singulink.Collections.Weak.csproj
+++ b/Source/Singulink.Collections.Weak/Singulink.Collections.Weak.csproj
@@ -1,40 +1,19 @@
-
+
netstandard2.0;netstandard2.1;net6.0
Singulink.Collections
- Singulink
- MIT
- Singulink Icon 128x128.png
- https://github.com/Singulink/Singulink.Collections.Weak
+
+ 2.0
weak, dictionary, value, collection, list
Collection classes that store weak references to values so that they can be garbage collected when they are no longer needed.
- © Singulink. All rights reserved.
+ https://github.com/Singulink/Singulink.Collections.Weak
+
true
key.snk
true
- 2.0
-
- true
- true
- true
- true
-
-
-
-
-
-
-
-
-
- True
-
-
-
-
-
-
+
+
diff --git a/Source/Singulink.Collections.Weak/WeakCollection.cs b/Source/Singulink.Collections.Weak/WeakCollection.cs
index 0c666a2..71ffdc4 100644
--- a/Source/Singulink.Collections.Weak/WeakCollection.cs
+++ b/Source/Singulink.Collections.Weak/WeakCollection.cs
@@ -1,204 +1,203 @@
using System.Collections;
-namespace Singulink.Collections
+namespace Singulink.Collections;
+
+///
+/// Represents a collection of weakly referenced values that keeps items in an undefined order. If this collection is accessed concurrently from multiple
+/// threads (even in a read-only manner) then all accesses must be synchronized with a full lock.
+///
+///
+/// On .NET Core 3+, internal entries for garbage collected values are removed as they are encountered (i.e. as they are enumerated over). This is not
+/// the case on .NET Standard targets like .NET Framework. You can perform a full clean by calling the method or configure automatic
+/// cleaning after a set number of add operations by setting the property.
+///
+public sealed class WeakCollection : IEnumerable where T : class
{
+ private readonly HashSet> _entries = [];
+
+ private int _autoCleanAddCount;
+ private int _addCountSinceLastClean;
+
///
- /// Represents a collection of weakly referenced values that keeps items in an undefined order. If this collection is accessed concurrently from multiple
- /// threads (even in a read-only manner) then all accesses must be synchronized with a full lock.
+ /// Initializes a new instance of the class.
///
- ///
- /// On .NET Core 3+, internal entries for garbage collected values are removed as they are encountered (i.e. as they are enumerated over). This is not
- /// the case on .NET Standard targets like .NET Framework. You can perform a full clean by calling the method or configure automatic
- /// cleaning after a set number of add operations by setting the property.
- ///
- public sealed class WeakCollection : IEnumerable where T : class
+ public WeakCollection()
{
- private readonly HashSet> _entries = new();
+ }
- private int _autoCleanAddCount;
- private int _addCountSinceLastClean;
+ ///
+ /// Gets or sets the number of operations that automatically triggers the method to run. Default value is
+ /// which indicates that automatic cleaning is not performed.
+ ///
+ public int? AutoCleanAddCount
+ {
+ get => _autoCleanAddCount == 0 ? null : _autoCleanAddCount;
+ set {
+ if (value < 1)
+ throw new ArgumentOutOfRangeException(nameof(value));
- ///
- /// Initializes a new instance of the class.
- ///
- public WeakCollection()
- {
+ _autoCleanAddCount = value.GetValueOrDefault();
}
+ }
- ///
- /// Gets or sets the number of operations that automatically triggers the method to run. Default value is
- /// which indicates that automatic cleaning is not performed.
- ///
- public int? AutoCleanAddCount
- {
- get => _autoCleanAddCount == 0 ? null : _autoCleanAddCount;
- set {
- if (value < 1)
- throw new ArgumentOutOfRangeException(nameof(value));
+ ///
+ /// Gets the number of add operations that have been performed since the last cleaning.
+ ///
+ public int AddCountSinceLastClean => _addCountSinceLastClean;
- _autoCleanAddCount = value.GetValueOrDefault();
- }
- }
+ ///
+ /// Gets or sets a value indicating whether to automatically call whenever is called. Default value is
+ /// .
+ ///
+ public bool TrimExcessDuringClean { get; set; }
- ///
- /// Gets the number of add operations that have been performed since the last cleaning.
- ///
- public int AddCountSinceLastClean => _addCountSinceLastClean;
-
- ///
- /// Gets or sets a value indicating whether to automatically call whenever is called. Default value is
- /// .
- ///
- public bool TrimExcessDuringClean { get; set; }
-
- ///
- /// Gets the number of entries in the internal data structure. This value will be higher than the actual number of values in the collection if any of
- /// the values were garbage collected but still have internal entries in the collection that have not been cleaned.
- ///
- ///
- /// This count will not be accurate if values have been collected since the last clean. You can call to force a full sweep
- /// before reading the count to get a more accurate value, but keep in mind that a subsequent enumeration may still return fewer values if they happen
- /// to get garbage collected before or during the enumeration. If you require an accurate count together with all the values then you should
- /// temporarily copy the values into a strongly referenced collection (like a ) so that they can't be garbage collected and use
- /// that to get the count and access the values.
- ///
- public int UnsafeCount => _entries.Count;
-
- ///
- /// Adds an item to the collection.
- ///
- public void Add(T item)
- {
- _entries.Add(new WeakReference(item));
- _addCountSinceLastClean++;
+ ///
+ /// Gets the number of entries in the internal data structure. This value will be higher than the actual number of values in the collection if any of
+ /// the values were garbage collected but still have internal entries in the collection that have not been cleaned.
+ ///
+ ///
+ /// This count will not be accurate if values have been collected since the last clean. You can call to force a full sweep
+ /// before reading the count to get a more accurate value, but keep in mind that a subsequent enumeration may still return fewer values if they happen
+ /// to get garbage collected before or during the enumeration. If you require an accurate count together with all the values then you should
+ /// temporarily copy the values into a strongly referenced collection (like a ) so that they can't be garbage collected and use
+ /// that to get the count and access the values.
+ ///
+ public int UnsafeCount => _entries.Count;
- if (_autoCleanAddCount != 0 && _addCountSinceLastClean >= _autoCleanAddCount)
- Clean();
- }
+ ///
+ /// Adds an item to the collection.
+ ///
+ public void Add(T item)
+ {
+ _entries.Add(new WeakReference(item));
+ _addCountSinceLastClean++;
- ///
- /// Removes an item from the collection using the specified equality comparer.
- ///
- /// if the item was removed, otherwise .
- public bool Remove(T item, IEqualityComparer? comparer = null)
- {
- comparer ??= EqualityComparer.Default;
+ if (_autoCleanAddCount != 0 && _addCountSinceLastClean >= _autoCleanAddCount)
+ Clean();
+ }
+
+ ///
+ /// Removes an item from the collection using the specified equality comparer.
+ ///
+ /// if the item was removed, otherwise .
+ public bool Remove(T item, IEqualityComparer? comparer = null)
+ {
+ comparer ??= EqualityComparer.Default;
- foreach (var entry in _entries)
+ foreach (var entry in _entries)
+ {
+ if (entry.TryGetTarget(out var value))
{
- if (entry.TryGetTarget(out var value))
- {
- if (comparer.Equals(value, item))
- {
- _entries.Remove(entry);
- return true;
- }
- }
-#if NET
- else
+ if (comparer.Equals(value, item))
{
_entries.Remove(entry);
+ return true;
}
-#endif
}
-
- return false;
+#if NET
+ else
+ {
+ _entries.Remove(entry);
+ }
+#endif
}
- ///
- /// Determines whether the collection contains the given item using the specified equality comparer.
- ///
- public bool Contains(T item, IEqualityComparer? comparer = null)
- {
- comparer ??= EqualityComparer.Default;
+ return false;
+ }
+
+ ///
+ /// Determines whether the collection contains the given item using the specified equality comparer.
+ ///
+ public bool Contains(T item, IEqualityComparer? comparer = null)
+ {
+ comparer ??= EqualityComparer.Default;
- foreach (var entry in _entries)
+ foreach (var entry in _entries)
+ {
+ if (entry.TryGetTarget(out var value))
{
- if (entry.TryGetTarget(out var value))
- {
- if (comparer.Equals(value, item))
- return true;
- }
+ if (comparer.Equals(value, item))
+ return true;
+ }
#if NET
- else {
- _entries.Remove(entry);
- }
-#endif
+ else {
+ _entries.Remove(entry);
}
-
- return false;
+#endif
}
- ///
- /// Removes all the elements from the collection.
- ///
- public void Clear()
- {
- _entries.Clear();
- _addCountSinceLastClean = 0;
- }
+ return false;
+ }
- ///
- /// Removes internal entries for values that have been garbage collected and trims the excess if is set.
- ///
- public void Clean()
- {
+ ///
+ /// Removes all the elements from the collection.
+ ///
+ public void Clear()
+ {
+ _entries.Clear();
+ _addCountSinceLastClean = 0;
+ }
+
+ ///
+ /// Removes internal entries for values that have been garbage collected and trims the excess if is set.
+ ///
+ public void Clean()
+ {
#if NET
- var staleEntries = _entries.Where(e => !e.TryGetTarget(out _));
+ var staleEntries = _entries.Where(e => !e.TryGetTarget(out _));
#else
- var staleEntries = _entries.Where(e => !e.TryGetTarget(out _)).ToList();
+ var staleEntries = _entries.Where(e => !e.TryGetTarget(out _)).ToList();
#endif
- foreach (var entry in staleEntries)
- _entries.Remove(entry);
+ foreach (var entry in staleEntries)
+ _entries.Remove(entry);
- if (TrimExcessDuringClean)
- TrimExcess();
+ if (TrimExcessDuringClean)
+ TrimExcess();
- _addCountSinceLastClean = 0;
- }
+ _addCountSinceLastClean = 0;
+ }
- ///
- /// Reduces the internal capacity to the number of entries in the collection.
- ///
- public void TrimExcess() => _entries.TrimExcess();
-
- ///
- /// Ensures that this collection can hold the specified number of elements without growing.
- ///
- ///
- /// This method has no effect on .NET Framework.
- ///
- public void EnsureCapacity(int capacity)
- {
+ ///
+ /// Reduces the internal capacity to the number of entries in the collection.
+ ///
+ public void TrimExcess() => _entries.TrimExcess();
+
+ ///
+ /// Ensures that this collection can hold the specified number of elements without growing.
+ ///
+ ///
+ /// This method has no effect on .NET Framework.
+ ///
+ public void EnsureCapacity(int capacity)
+ {
#if !NETSTANDARD2_0
- _entries.EnsureCapacity(capacity);
+ _entries.EnsureCapacity(capacity);
#endif
- }
+ }
- ///
- /// Returns an enumerator that iterates through the collection.
- ///
- public IEnumerator GetEnumerator()
+ ///
+ /// Returns an enumerator that iterates through the collection.
+ ///
+ public IEnumerator GetEnumerator()
+ {
+ foreach (var entry in _entries)
{
- foreach (var entry in _entries)
- {
- if (entry.TryGetTarget(out var item))
- yield return item;
+ if (entry.TryGetTarget(out var item))
+ yield return item;
#if NET
- else
- _entries.Remove(entry);
+ else
+ _entries.Remove(entry);
#endif
- }
+ }
#if NET
- _addCountSinceLastClean = 0;
+ _addCountSinceLastClean = 0;
#endif
- }
-
- ///
- /// Returns an enumerator that iterates through the collection.
- ///
- IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
+
+ ///
+ /// Returns an enumerator that iterates through the collection.
+ ///
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
\ No newline at end of file
diff --git a/Source/Singulink.Collections.Weak/WeakList.cs b/Source/Singulink.Collections.Weak/WeakList.cs
index 34274cf..24904f2 100644
--- a/Source/Singulink.Collections.Weak/WeakList.cs
+++ b/Source/Singulink.Collections.Weak/WeakList.cs
@@ -1,277 +1,276 @@
using System.Collections;
-namespace Singulink.Collections
+namespace Singulink.Collections;
+
+///
+/// Represents a collection of weakly referenced values that maintains relative insertion order. If this collection is accessed concurrently from multiple
+/// threads in a read-only manner then no locking is necessary, otherwise a full lock or reader/writer lock must be obtained around all accesses.
+///
+///
+/// Internal entries for garbage collected values are not removed automatically by default. You can perform a full clean by calling the method or configure automatic cleaning after a set number of operations by setting the property.
+///
+public sealed class WeakList : IEnumerable where T : class
{
+ private readonly List> _entries = [];
+
+ private int _autoCleanAddCount;
+ private int _extraTrimCapacity;
+ private int _addCountSinceLastClean;
+
///
- /// Represents a collection of weakly referenced values that maintains relative insertion order. If this collection is accessed concurrently from multiple
- /// threads in a read-only manner then no locking is necessary, otherwise a full lock or reader/writer lock must be obtained around all accesses.
+ /// Initializes a new instance of the class.
///
- ///
- /// Internal entries for garbage collected values are not removed automatically by default. You can perform a full clean by calling the method or configure automatic cleaning after a set number of operations by setting the property.
- ///
- public sealed class WeakList : IEnumerable where T : class
+ public WeakList()
{
- private readonly List> _entries = new List>();
+ }
- private int _autoCleanAddCount;
- private int _extraTrimCapacity;
- private int _addCountSinceLastClean;
+ ///
+ /// Gets or sets the number of operations that automatically triggers the method to run. Default value is
+ /// which indicates that automatic cleaning is not performed.
+ ///
+ public int? AutoCleanAddCount
+ {
+ get => _autoCleanAddCount == 0 ? null : _autoCleanAddCount;
+ set {
+ if (value < 1)
+ throw new ArgumentOutOfRangeException(nameof(value));
- ///
- /// Initializes a new instance of the class.
- ///
- public WeakList()
- {
+ _autoCleanAddCount = value.GetValueOrDefault();
}
+ }
- ///
- /// Gets or sets the number of operations that automatically triggers the method to run. Default value is
- /// which indicates that automatic cleaning is not performed.
- ///
- public int? AutoCleanAddCount
- {
- get => _autoCleanAddCount == 0 ? null : _autoCleanAddCount;
- set {
- if (value < 1)
- throw new ArgumentOutOfRangeException(nameof(value));
-
- _autoCleanAddCount = value.GetValueOrDefault();
- }
- }
+ ///
+ /// Gets the number of add operations that have been performed since the last cleaning.
+ ///
+ public int AddCountSinceLastClean => _addCountSinceLastClean;
- ///
- /// Gets the number of add operations that have been performed since the last cleaning.
- ///
- public int AddCountSinceLastClean => _addCountSinceLastClean;
-
- ///
- /// Gets or sets a value indicating whether to automatically call whenever is called. Default value is
- /// .
- ///
- public bool TrimExcessDuringClean { get; set; }
-
- ///
- /// Gets or sets the extra capacity to leave when is called. Default value is 0.
- ///
- public int ExtraTrimCapacity
- {
- get => _extraTrimCapacity;
- set {
- if (_extraTrimCapacity < 0)
- throw new ArgumentOutOfRangeException(nameof(value));
+ ///
+ /// Gets or sets a value indicating whether to automatically call whenever is called. Default value is
+ /// .
+ ///
+ public bool TrimExcessDuringClean { get; set; }
- _extraTrimCapacity = value;
- }
- }
+ ///
+ /// Gets or sets the extra capacity to leave when is called. Default value is 0.
+ ///
+ public int ExtraTrimCapacity
+ {
+ get => _extraTrimCapacity;
+ set {
+ if (_extraTrimCapacity < 0)
+ throw new ArgumentOutOfRangeException(nameof(value));
- ///
- /// Gets the number of entries in the internal data structure. This value will be different than the actual count if any of the values were garbage
- /// collected but still have internal entries in the list that have not been cleaned.
- ///
- ///
- /// This count will not be accurate if values have been collected since the last clean. You can call to force a full sweep
- /// before reading the count to get a more accurate value, but keep in mind that a subsequent enumeration may still return fewer values if they happen
- /// to get garbage collected before or during the enumeration. If you require an accurate count together with all the values then you should
- /// temporarily copy the values into a strongly referenced collection (like a ) so that they can't be garbage collected and use
- /// that to get the count and access the values.
- ///
- public int UnsafeCount => _entries.Count;
-
- ///
- /// Gets or sets the total number of elements the internal data structure can hold without resizing.
- ///
- public int Capacity
- {
- get => _entries.Capacity;
- set => _entries.Capacity = value;
+ _extraTrimCapacity = value;
}
+ }
- ///
- /// Adds an item to the end of the collection.
- ///
- public void Add(T item)
- {
- _entries.Add(new WeakReference(item));
- OnAdded();
- }
+ ///
+ /// Gets the number of entries in the internal data structure. This value will be different than the actual count if any of the values were garbage
+ /// collected but still have internal entries in the list that have not been cleaned.
+ ///
+ ///
+ /// This count will not be accurate if values have been collected since the last clean. You can call to force a full sweep
+ /// before reading the count to get a more accurate value, but keep in mind that a subsequent enumeration may still return fewer values if they happen
+ /// to get garbage collected before or during the enumeration. If you require an accurate count together with all the values then you should
+ /// temporarily copy the values into a strongly referenced collection (like a ) so that they can't be garbage collected and use
+ /// that to get the count and access the values.
+ ///
+ public int UnsafeCount => _entries.Count;
- ///
- /// Inserts an item to the beginning of the collection.
- ///
- public void InsertFirst(T item)
- {
- _entries.Insert(0, new WeakReference(item));
- OnAdded();
- }
+ ///
+ /// Gets or sets the total number of elements the internal data structure can hold without resizing.
+ ///
+ public int Capacity
+ {
+ get => _entries.Capacity;
+ set => _entries.Capacity = value;
+ }
- ///
- /// Inserts an item before another item.
- ///
- /// The item to find to determine the insertion point.
- /// The item to insert.
- /// The comparer to use to determine item equality.
- /// The item to insert before was not found.
- public void InsertBefore(T findItem, T item, IEqualityComparer? comparer = null)
- {
- if (!TryInsertBefore(findItem, item, comparer))
- throw new ArgumentException("The specified item was not found.", nameof(findItem));
- }
+ ///
+ /// Adds an item to the end of the collection.
+ ///
+ public void Add(T item)
+ {
+ _entries.Add(new WeakReference(item));
+ OnAdded();
+ }
- ///
- /// Inserts an item before another item.
- ///
- /// The item to find to determine the insertion point.
- /// The item to insert.
- /// The comparer to use to determine item equality.
- /// if the item to insert after was found and the item was inserted, otherwise .
- public bool TryInsertBefore(T findItem, T item, IEqualityComparer? comparer = null)
- {
- comparer ??= EqualityComparer.Default;
+ ///
+ /// Inserts an item to the beginning of the collection.
+ ///
+ public void InsertFirst(T item)
+ {
+ _entries.Insert(0, new WeakReference(item));
+ OnAdded();
+ }
- for (int i = 0; i < _entries.Count; i++)
- {
- if (_entries[i].TryGetTarget(out var currentItem) && comparer.Equals(currentItem, findItem))
- {
- _entries.Insert(i, new WeakReference(item));
- OnAdded();
- return true;
- }
- }
+ ///
+ /// Inserts an item before another item.
+ ///
+ /// The item to find to determine the insertion point.
+ /// The item to insert.
+ /// The comparer to use to determine item equality.
+ /// The item to insert before was not found.
+ public void InsertBefore(T findItem, T item, IEqualityComparer? comparer = null)
+ {
+ if (!TryInsertBefore(findItem, item, comparer))
+ throw new ArgumentException("The specified item was not found.", nameof(findItem));
+ }
- return false;
- }
+ ///
+ /// Inserts an item before another item.
+ ///
+ /// The item to find to determine the insertion point.
+ /// The item to insert.
+ /// The comparer to use to determine item equality.
+ /// if the item to insert after was found and the item was inserted, otherwise .
+ public bool TryInsertBefore(T findItem, T item, IEqualityComparer? comparer = null)
+ {
+ comparer ??= EqualityComparer.Default;
- ///
- /// Inserts an item after another item.
- ///
- /// The item to find to determine the insertion point.
- /// The item to insert.
- /// The comparer to use to determine item equality.
- /// The item to insert after was not found.
- public void InsertAfter(T findItem, T item, IEqualityComparer? comparer = null)
+ for (int i = 0; i < _entries.Count; i++)
{
- if (!TryInsertAfter(findItem, item, comparer))
- throw new ArgumentException("The specified item was not found.", nameof(findItem));
+ if (_entries[i].TryGetTarget(out var currentItem) && comparer.Equals(currentItem, findItem))
+ {
+ _entries.Insert(i, new WeakReference(item));
+ OnAdded();
+ return true;
+ }
}
- ///
- /// Inserts an item after another item.
- ///
- /// The item to find to determine the insertion point.
- /// The item to insert.
- /// The comparer to use to determine item equality.
- /// if the item to insert after was found and the item was inserted, otherwise .
- public bool TryInsertAfter(T findItem, T item, IEqualityComparer? comparer = null)
- {
- comparer ??= EqualityComparer.Default;
+ return false;
+ }
- for (int i = 0; i < _entries.Count; i++)
- {
- if (_entries[i].TryGetTarget(out var currentItem) && comparer.Equals(currentItem, findItem))
- {
- _entries.Insert(i + 1, new WeakReference(item));
- OnAdded();
- return true;
- }
- }
+ ///
+ /// Inserts an item after another item.
+ ///
+ /// The item to find to determine the insertion point.
+ /// The item to insert.
+ /// The comparer to use to determine item equality.
+ /// The item to insert after was not found.
+ public void InsertAfter(T findItem, T item, IEqualityComparer? comparer = null)
+ {
+ if (!TryInsertAfter(findItem, item, comparer))
+ throw new ArgumentException("The specified item was not found.", nameof(findItem));
+ }
- return false;
- }
+ ///
+ /// Inserts an item after another item.
+ ///
+ /// The item to find to determine the insertion point.
+ /// The item to insert.
+ /// The comparer to use to determine item equality.
+ /// if the item to insert after was found and the item was inserted, otherwise .
+ public bool TryInsertAfter(T findItem, T item, IEqualityComparer? comparer = null)
+ {
+ comparer ??= EqualityComparer.Default;
- ///
- /// Removes an item from the collection using the specified equality comparer.
- ///
- /// if the item was removed, otherwise .
- public bool Remove(T item, IEqualityComparer? comparer = null)
+ for (int i = 0; i < _entries.Count; i++)
{
- comparer ??= EqualityComparer.Default;
-
- for (int i = 0; i < _entries.Count; i++)
+ if (_entries[i].TryGetTarget(out var currentItem) && comparer.Equals(currentItem, findItem))
{
- if (_entries[i].TryGetTarget(out var currentItem) && comparer.Equals(currentItem, item))
- {
- _entries.RemoveAt(i);
- return true;
- }
+ _entries.Insert(i + 1, new WeakReference(item));
+ OnAdded();
+ return true;
}
-
- return false;
}
- ///
- /// Determines whether the collection contains the given item using the specified equality comparer.
- ///
- public bool Contains(T item, IEqualityComparer? comparer = null)
- {
- comparer ??= EqualityComparer.Default;
+ return false;
+ }
+
+ ///
+ /// Removes an item from the collection using the specified equality comparer.
+ ///
+ /// if the item was removed, otherwise .
+ public bool Remove(T item, IEqualityComparer? comparer = null)
+ {
+ comparer ??= EqualityComparer.Default;
- foreach (var entry in _entries)
+ for (int i = 0; i < _entries.Count; i++)
+ {
+ if (_entries[i].TryGetTarget(out var currentItem) && comparer.Equals(currentItem, item))
{
- if (entry.TryGetTarget(out var currentItem) && comparer.Equals(currentItem, item))
- return true;
+ _entries.RemoveAt(i);
+ return true;
}
-
- return false;
}
- ///
- /// Removes all the elements from the collection.
- ///
- public void Clear()
+ return false;
+ }
+
+ ///
+ /// Determines whether the collection contains the given item using the specified equality comparer.
+ ///
+ public bool Contains(T item, IEqualityComparer? comparer = null)
+ {
+ comparer ??= EqualityComparer.Default;
+
+ foreach (var entry in _entries)
{
- _entries.Clear();
- _addCountSinceLastClean = 0;
+ if (entry.TryGetTarget(out var currentItem) && comparer.Equals(currentItem, item))
+ return true;
}
- ///
- /// Removes internal entries for values that have been garbage collected and trims the excess if is set.
- ///
- public void Clean()
- {
- _entries.RemoveAll(entry => !entry.TryGetTarget(out _));
+ return false;
+ }
- if (TrimExcessDuringClean)
- TrimExcess();
+ ///
+ /// Removes all the elements from the collection.
+ ///
+ public void Clear()
+ {
+ _entries.Clear();
+ _addCountSinceLastClean = 0;
+ }
- _addCountSinceLastClean = 0;
- }
+ ///
+ /// Removes internal entries for values that have been garbage collected and trims the excess if is set.
+ ///
+ public void Clean()
+ {
+ _entries.RemoveAll(entry => !entry.TryGetTarget(out _));
- ///
- /// Reduces the internal capacity to the number of entries in the collection plus .
- ///
- public void TrimExcess()
- {
- int trimmedCapacity = _entries.Count + ExtraTrimCapacity;
+ if (TrimExcessDuringClean)
+ TrimExcess();
- if (trimmedCapacity < _entries.Capacity)
- _entries.Capacity = trimmedCapacity;
- }
+ _addCountSinceLastClean = 0;
+ }
- ///
- /// Returns an enumerator that iterates through the collection.
- ///
- public IEnumerator GetEnumerator()
- {
- foreach (var entry in _entries)
- {
- if (entry.TryGetTarget(out var item))
- yield return item;
- }
- }
+ ///
+ /// Reduces the internal capacity to the number of entries in the collection plus .
+ ///
+ public void TrimExcess()
+ {
+ int trimmedCapacity = _entries.Count + ExtraTrimCapacity;
- private void OnAdded()
- {
- _addCountSinceLastClean++;
+ if (trimmedCapacity < _entries.Capacity)
+ _entries.Capacity = trimmedCapacity;
+ }
- if (_autoCleanAddCount != 0 && _addCountSinceLastClean >= _autoCleanAddCount)
- Clean();
+ ///
+ /// Returns an enumerator that iterates through the collection.
+ ///
+ public IEnumerator GetEnumerator()
+ {
+ foreach (var entry in _entries)
+ {
+ if (entry.TryGetTarget(out var item))
+ yield return item;
}
+ }
+
+ private void OnAdded()
+ {
+ _addCountSinceLastClean++;
- ///
- /// Returns an enumerator that iterates through the collection.
- ///
- IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+ if (_autoCleanAddCount != 0 && _addCountSinceLastClean >= _autoCleanAddCount)
+ Clean();
}
+
+ ///
+ /// Returns an enumerator that iterates through the collection.
+ ///
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
\ No newline at end of file
diff --git a/Source/Singulink.Collections.Weak/WeakValueDictionary.cs b/Source/Singulink.Collections.Weak/WeakValueDictionary.cs
index 77747ac..317c774 100644
--- a/Source/Singulink.Collections.Weak/WeakValueDictionary.cs
+++ b/Source/Singulink.Collections.Weak/WeakValueDictionary.cs
@@ -1,337 +1,336 @@
using System.Collections;
using System.Diagnostics.CodeAnalysis;
-namespace Singulink.Collections
+namespace Singulink.Collections;
+
+///
+/// Represents a collection of keys and weakly referenced values. If this collection is accessed concurrently from multiple threads (even in a read-only
+/// manner) then all accesses must be synchronized with a full lock.
+///
+///
+/// On .NET, internal entries for garbage collected values are cleaned as they are encountered (i.e. when a key lookup is performed on a garbage collected
+/// value or key/value pairs are enumerated over). This is not the case on .NET Framework. You can perform a full clean by calling the
+/// method or configure automatic cleaning after a set number of add operations by setting the property.
+///
+public class WeakValueDictionary : IEnumerable>
+ where TKey : notnull
+ where TValue : class
{
- ///
- /// Represents a collection of keys and weakly referenced values. If this collection is accessed concurrently from multiple threads (even in a read-only
- /// manner) then all accesses must be synchronized with a full lock.
- ///
- ///
- /// On .NET, internal entries for garbage collected values are cleaned as they are encountered (i.e. when a key lookup is performed on a garbage collected
- /// value or key/value pairs are enumerated over). This is not the case on .NET Framework. You can perform a full clean by calling the
- /// method or configure automatic cleaning after a set number of add operations by setting the property.
- ///
- public class WeakValueDictionary : IEnumerable>
- where TKey : notnull
- where TValue : class
- {
#if NETSTANDARD2_0
- private Dictionary> _entryLookup;
- private int _capacity;
+ private Dictionary> _entryLookup;
+ private int _capacity;
#else
- private readonly Dictionary> _entryLookup;
+ private readonly Dictionary> _entryLookup;
#endif
- private int _autoCleanAddCount;
- private int _addCountSinceLastClean;
+ private int _autoCleanAddCount;
+ private int _addCountSinceLastClean;
- ///
- /// Initializes a new instance of the class.
- ///
- public WeakValueDictionary() : this(null)
- {
- }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public WeakValueDictionary() : this(null)
+ {
+ }
- ///
- /// Initializes a new instance of the class using the specified key equality comparer.
- ///
- public WeakValueDictionary(IEqualityComparer? comparer)
- {
- _entryLookup = new(comparer);
- }
+ ///
+ /// Initializes a new instance of the class using the specified key equality comparer.
+ ///
+ public WeakValueDictionary(IEqualityComparer? comparer)
+ {
+ _entryLookup = new(comparer);
+ }
- ///
- /// Gets or sets the number of add (or indexer set) operations that automatically triggers the method to run. Default value is
- /// which indicates that automatic cleaning is not performed.
- ///
- public int? AutoCleanAddCount
- {
- get => _autoCleanAddCount == 0 ? null : _autoCleanAddCount;
- set {
- if (value < 1)
- throw new ArgumentOutOfRangeException(nameof(value));
+ ///
+ /// Gets or sets the number of add (or indexer set) operations that automatically triggers the method to run. Default value is
+ /// which indicates that automatic cleaning is not performed.
+ ///
+ public int? AutoCleanAddCount
+ {
+ get => _autoCleanAddCount == 0 ? null : _autoCleanAddCount;
+ set {
+ if (value < 1)
+ throw new ArgumentOutOfRangeException(nameof(value));
- _autoCleanAddCount = value.GetValueOrDefault();
- }
+ _autoCleanAddCount = value.GetValueOrDefault();
}
+ }
- ///
- /// Gets the number of add (or indexer set) operations that have been performed since the last cleaning.
- ///
- public int AddCountSinceLastClean => _addCountSinceLastClean;
-
- ///
- /// Gets or sets a value indicating whether to automatically call whenever is called. Default value is
- /// .
- ///
- public bool TrimExcessDuringClean { get; set; }
-
- ///
- /// Gets the keys in the dictionary.
- ///
- public IEnumerable Keys => this.Select(kvp => kvp.Key);
-
- ///
- /// Gets the values in the dictionary.
- ///
- public IEnumerable Values => this.Select(kvp => kvp.Value);
-
- ///
- /// Gets the number of entries in the internal data structure. This value will be different than the actual count if any of the values were garbage
- /// collected but still have internal entries in the dictionary that have not been cleaned.
- ///
- ///
- /// This count will not be accurate if values have been collected since the last clean. You can call to force a full sweep
- /// before reading the count to get a more accurate value, but keep in mind that a subsequent enumeration may still return fewer values if they happen
- /// to get garbage collected before or during the enumeration. If you require an accurate count together with all the values then you should
- /// temporarily copy the values into a strongly referenced collection (like a or ) so that
- /// they can't be garbage collected and use that to get the count and access the values.
- ///
- public int UnsafeCount => _entryLookup.Count;
-
- ///
- /// Gets or sets the value associated with the specified key.
- ///
- public TValue this[TKey key]
- {
- get {
- if (!TryGetValue(key, out var value))
- throw new KeyNotFoundException();
+ ///
+ /// Gets the number of add (or indexer set) operations that have been performed since the last cleaning.
+ ///
+ public int AddCountSinceLastClean => _addCountSinceLastClean;
- return value;
- }
- set {
- _entryLookup[key] = new WeakReference(value);
- OnAdded();
- }
- }
+ ///
+ /// Gets or sets a value indicating whether to automatically call whenever is called. Default value is
+ /// .
+ ///
+ public bool TrimExcessDuringClean { get; set; }
- ///
- /// Gets the value associated with the specified key.
- ///
- /// The key of the value to get.
- /// The value associated with the specified key, otherwise .
- /// if the dictionary contains a value with the specified key, otherwise .
- public bool TryGetValue(TKey key, [NotNullWhen(true)] out TValue? value)
- {
- if (_entryLookup.TryGetValue(key, out var entry))
- {
- if (entry.TryGetTarget(out value))
- return true;
-#if NET
- else
- _entryLookup.Remove(key);
-#endif
- }
+ ///
+ /// Gets the keys in the dictionary.
+ ///
+ public IEnumerable Keys => this.Select(kvp => kvp.Key);
- value = null;
- return false;
- }
+ ///
+ /// Gets the values in the dictionary.
+ ///
+ public IEnumerable Values => this.Select(kvp => kvp.Value);
- ///
- /// Adds the specified key and value to the dictionary.
- ///
- public bool TryAdd(TKey key, TValue value)
- {
- if (_entryLookup.TryGetValue(key, out var entry))
- {
- if (entry.TryGetTarget(out var _))
- return false;
+ ///
+ /// Gets the number of entries in the internal data structure. This value will be different than the actual count if any of the values were garbage
+ /// collected but still have internal entries in the dictionary that have not been cleaned.
+ ///
+ ///
+ /// This count will not be accurate if values have been collected since the last clean. You can call to force a full sweep
+ /// before reading the count to get a more accurate value, but keep in mind that a subsequent enumeration may still return fewer values if they happen
+ /// to get garbage collected before or during the enumeration. If you require an accurate count together with all the values then you should
+ /// temporarily copy the values into a strongly referenced collection (like a or ) so that
+ /// they can't be garbage collected and use that to get the count and access the values.
+ ///
+ public int UnsafeCount => _entryLookup.Count;
- entry.SetTarget(value);
- }
- else
- {
- _entryLookup.Add(key, new WeakReference(value));
- }
+ ///
+ /// Gets or sets the value associated with the specified key.
+ ///
+ public TValue this[TKey key]
+ {
+ get {
+ if (!TryGetValue(key, out var value))
+ throw new KeyNotFoundException();
+ return value;
+ }
+ set {
+ _entryLookup[key] = new WeakReference(value);
OnAdded();
- return true;
}
+ }
- ///
- /// Adds the specified key and value to the dictionary.
- ///
- /// The specified key already exists in the dictionary.
- public void Add(TKey key, TValue value)
+ ///
+ /// Gets the value associated with the specified key.
+ ///
+ /// The key of the value to get.
+ /// The value associated with the specified key, otherwise .
+ /// if the dictionary contains a value with the specified key, otherwise .
+ public bool TryGetValue(TKey key, [NotNullWhen(true)] out TValue? value)
+ {
+ if (_entryLookup.TryGetValue(key, out var entry))
{
- if (!TryAdd(key, value))
- throw new ArgumentException("Specified key already exists.", nameof(key));
+ if (entry.TryGetTarget(out value))
+ return true;
+#if NET
+ else
+ _entryLookup.Remove(key);
+#endif
}
- ///
- /// Removes the value with the specified key from the dictionary.
- ///
- /// if the item was found and removed, otherwise .
- public bool Remove(TKey key)
+ value = null;
+ return false;
+ }
+
+ ///
+ /// Adds the specified key and value to the dictionary.
+ ///
+ public bool TryAdd(TKey key, TValue value)
+ {
+ if (_entryLookup.TryGetValue(key, out var entry))
{
- if (_entryLookup.TryGetValue(key, out var entry))
- {
- _entryLookup.Remove(key);
- return entry.TryGetTarget(out _);
- }
+ if (entry.TryGetTarget(out var _))
+ return false;
- return false;
+ entry.SetTarget(value);
+ }
+ else
+ {
+ _entryLookup.Add(key, new WeakReference(value));
}
- ///
- /// Removes the value with the specified key from the dictionary.
- ///
- /// The key of the value to remove.
- /// The value that was removed, otherwise .
- /// if the item was found and removed, otherwise .
- public bool Remove(TKey key, [MaybeNullWhen(false)] out TValue value)
+ OnAdded();
+ return true;
+ }
+
+ ///
+ /// Adds the specified key and value to the dictionary.
+ ///
+ /// The specified key already exists in the dictionary.
+ public void Add(TKey key, TValue value)
+ {
+ if (!TryAdd(key, value))
+ throw new ArgumentException("Specified key already exists.", nameof(key));
+ }
+
+ ///
+ /// Removes the value with the specified key from the dictionary.
+ ///
+ /// if the item was found and removed, otherwise .
+ public bool Remove(TKey key)
+ {
+ if (_entryLookup.TryGetValue(key, out var entry))
{
- if (_entryLookup.TryGetValue(key, out var entry))
- {
- _entryLookup.Remove(key);
- return entry.TryGetTarget(out value);
- }
+ _entryLookup.Remove(key);
+ return entry.TryGetTarget(out _);
+ }
+
+ return false;
+ }
- value = default;
- return false;
+ ///
+ /// Removes the value with the specified key from the dictionary.
+ ///
+ /// The key of the value to remove.
+ /// The value that was removed, otherwise .
+ /// if the item was found and removed, otherwise .
+ public bool Remove(TKey key, [MaybeNullWhen(false)] out TValue value)
+ {
+ if (_entryLookup.TryGetValue(key, out var entry))
+ {
+ _entryLookup.Remove(key);
+ return entry.TryGetTarget(out value);
}
- ///
- /// Removes the entry with the given key and value from the dictionary using the specified equality comparer for the value type.
- ///
- public bool Remove(TKey key, TValue value, IEqualityComparer? comparer = null)
+ value = default;
+ return false;
+ }
+
+ ///
+ /// Removes the entry with the given key and value from the dictionary using the specified equality comparer for the value type.
+ ///
+ public bool Remove(TKey key, TValue value, IEqualityComparer? comparer = null)
+ {
+ if (_entryLookup.TryGetValue(key, out var entry))
{
- if (_entryLookup.TryGetValue(key, out var entry))
+ if (entry.TryGetTarget(out var current))
{
- if (entry.TryGetTarget(out var current))
- {
- if ((comparer ?? EqualityComparer.Default).Equals(value, current))
- {
- _entryLookup.Remove(key);
- return true;
- }
- }
- else
+ if ((comparer ?? EqualityComparer.Default).Equals(value, current))
{
_entryLookup.Remove(key);
+ return true;
}
}
-
- return false;
+ else
+ {
+ _entryLookup.Remove(key);
+ }
}
- ///
- /// Indictes whether the dictionary contains the specified key/value pair using the optionally specified value comparer.
- ///
- public bool Contains(KeyValuePair kvp, IEqualityComparer? comparer = null) => Contains(kvp.Key, kvp.Value, comparer);
+ return false;
+ }
- ///
- /// Indictes whether the dictionary contains the key and value using the optionally specified value comparer.
- ///
- public bool Contains(TKey key, TValue value, IEqualityComparer? comparer = null)
- {
- return TryGetValue(key, out var current) && (comparer ?? EqualityComparer.Default).Equals(value, current);
- }
+ ///
+ /// Indictes whether the dictionary contains the specified key/value pair using the optionally specified value comparer.
+ ///
+ public bool Contains(KeyValuePair kvp, IEqualityComparer? comparer = null) => Contains(kvp.Key, kvp.Value, comparer);
- ///
- /// Determines whether the dictionary contains the specified key.
- ///
- public bool ContainsKey(TKey key) => TryGetValue(key, out _);
+ ///
+ /// Indictes whether the dictionary contains the key and value using the optionally specified value comparer.
+ ///
+ public bool Contains(TKey key, TValue value, IEqualityComparer? comparer = null)
+ {
+ return TryGetValue(key, out var current) && (comparer ?? EqualityComparer.Default).Equals(value, current);
+ }
- ///
- /// Determines whether the dictionary contains the specified value.
- ///
- public bool ContainsValue(TValue value, IEqualityComparer? comparer = null) => Values.Contains(value, comparer);
+ ///
+ /// Determines whether the dictionary contains the specified key.
+ ///
+ public bool ContainsKey(TKey key) => TryGetValue(key, out _);
- ///
- /// Removes all keys and values from the dictionary.
- ///
- public void Clear()
- {
- _entryLookup.Clear();
- _addCountSinceLastClean = 0;
- }
+ ///
+ /// Determines whether the dictionary contains the specified value.
+ ///
+ public bool ContainsValue(TValue value, IEqualityComparer? comparer = null) => Values.Contains(value, comparer);
- ///
- /// Removes internal entries that refer to values that have been garbage collected.
- ///
- public void Clean()
- {
+ ///
+ /// Removes all keys and values from the dictionary.
+ ///
+ public void Clear()
+ {
+ _entryLookup.Clear();
+ _addCountSinceLastClean = 0;
+ }
+
+ ///
+ /// Removes internal entries that refer to values that have been garbage collected.
+ ///
+ public void Clean()
+ {
#if NET
- var staleKvps = _entryLookup.Where(kvp => !kvp.Value.TryGetTarget(out _));
+ var staleKvps = _entryLookup.Where(kvp => !kvp.Value.TryGetTarget(out _));
#else
- var staleKvps = _entryLookup.Where(kvp => !kvp.Value.TryGetTarget(out _)).ToList();
+ var staleKvps = _entryLookup.Where(kvp => !kvp.Value.TryGetTarget(out _)).ToList();
#endif
- foreach (var kvp in staleKvps)
- _entryLookup.Remove(kvp.Key);
+ foreach (var kvp in staleKvps)
+ _entryLookup.Remove(kvp.Key);
- if (TrimExcessDuringClean)
- TrimExcess();
+ if (TrimExcessDuringClean)
+ TrimExcess();
- _addCountSinceLastClean = 0;
- }
+ _addCountSinceLastClean = 0;
+ }
- ///
- /// Reduces the internal capacity of this dictionary to the size needed to hold the current entries.
- ///
- public void TrimExcess()
- {
+ ///
+ /// Reduces the internal capacity of this dictionary to the size needed to hold the current entries.
+ ///
+ public void TrimExcess()
+ {
#if NETSTANDARD2_0
- if (_capacity > _entryLookup.Count * 2)
- {
- _entryLookup = new Dictionary>(_entryLookup, _entryLookup.Comparer);
- _capacity = _entryLookup.Count;
- }
+ if (_capacity > _entryLookup.Count * 2)
+ {
+ _entryLookup = new Dictionary>(_entryLookup, _entryLookup.Comparer);
+ _capacity = _entryLookup.Count;
+ }
#else
- _entryLookup.TrimExcess();
+ _entryLookup.TrimExcess();
#endif
- }
+ }
- ///
- /// Ensures that this dictionary can hold the specified number of elements without growing.
- ///
- ///
- /// This method has no effect on .NET Framework.
- ///
- public void EnsureCapacity(int capacity)
- {
+ ///
+ /// Ensures that this dictionary can hold the specified number of elements without growing.
+ ///
+ ///
+ /// This method has no effect on .NET Framework.
+ ///
+ public void EnsureCapacity(int capacity)
+ {
#if !NETSTANDARD2_0
- _entryLookup.EnsureCapacity(capacity);
+ _entryLookup.EnsureCapacity(capacity);
#endif
- }
+ }
- ///
- /// Returns an enumerator that iterates through the key/value pairs in the dictionary.
- ///
- public IEnumerator> GetEnumerator()
+ ///
+ /// Returns an enumerator that iterates through the key/value pairs in the dictionary.
+ ///
+ public IEnumerator> GetEnumerator()
+ {
+ foreach (var kvp in _entryLookup)
{
- foreach (var kvp in _entryLookup)
- {
- if (kvp.Value.TryGetTarget(out var value))
- yield return new KeyValuePair(kvp.Key, value);
+ if (kvp.Value.TryGetTarget(out var value))
+ yield return new KeyValuePair(kvp.Key, value);
#if NET
- else
- _entryLookup.Remove(kvp.Key);
+ else
+ _entryLookup.Remove(kvp.Key);
#endif
- }
+ }
#if NET
- _addCountSinceLastClean = 0;
+ _addCountSinceLastClean = 0;
#endif
- }
+ }
- private void OnAdded()
- {
+ private void OnAdded()
+ {
#if NETSTANDARD2_0
- if (_entryLookup.Count > _capacity)
- _capacity = _entryLookup.Count;
+ if (_entryLookup.Count > _capacity)
+ _capacity = _entryLookup.Count;
#endif
- _addCountSinceLastClean++;
+ _addCountSinceLastClean++;
- if (_autoCleanAddCount != 0 && _addCountSinceLastClean >= _autoCleanAddCount)
- Clean();
- }
-
- ///
- /// Returns an enumerator that iterates through the key/value pairs in the dictionary.
- ///
- IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+ if (_autoCleanAddCount != 0 && _addCountSinceLastClean >= _autoCleanAddCount)
+ Clean();
}
+
+ ///
+ /// Returns an enumerator that iterates through the key/value pairs in the dictionary.
+ ///
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
\ No newline at end of file
diff --git a/Source/Singulink.Collections.Weak/stylecop.json b/Source/Singulink.Collections.Weak/stylecop.json
deleted file mode 100644
index d4d297b..0000000
--- a/Source/Singulink.Collections.Weak/stylecop.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- // Enabling configuration: https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/EnableConfiguration.md
-
- "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
- "settings": {
- "documentationRules": {
- "documentExposedElements": true,
- "documentInternalElements": false,
- "documentInterfaces": false
- }
- }
-}
diff --git a/Source/Singulink.Collections.sln b/Source/Singulink.Collections.sln
deleted file mode 100644
index ace6c46..0000000
--- a/Source/Singulink.Collections.sln
+++ /dev/null
@@ -1,115 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.4.33103.184
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Singulink.Collections", "Singulink.Collections\Singulink.Collections.csproj", "{C802E214-0FE3-4115-A4E9-B08C8EE76759}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{79867FED-529B-4BE8-BF2B-7CF02BC48A28}"
- ProjectSection(SolutionItems) = preProject
- .editorconfig = .editorconfig
- ..\.github\workflows\build-and-test.yml = ..\.github\workflows\build-and-test.yml
- Directory.Build.props = Directory.Build.props
- Directory.Build.targets = Directory.Build.targets
- ..\README.md = ..\README.md
- EndProjectSection
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Singulink.Collections.Abstractions", "Singulink.Collections.Abstractions\Singulink.Collections.Abstractions.csproj", "{8EF2D844-0A43-47EF-B055-030D13F02088}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Singulink.Collections.Tests", "Tests\Singulink.Collections.Tests\Singulink.Collections.Tests.csproj", "{37173589-E411-4806-A79C-153D9659FAE7}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test Items", "Test Items", "{465CA120-1038-480C-893C-AE32B93017A3}"
- ProjectSection(SolutionItems) = preProject
- Tests\.editorconfig = Tests\.editorconfig
- Tests\Directory.Build.props = Tests\Directory.Build.props
- EndProjectSection
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{B95CC845-E2D3-4EF5-B7DF-FC902D58FF4C}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Singulink.Collections.Weak", "Singulink.Collections.Weak\Singulink.Collections.Weak.csproj", "{5EDDA4AB-D81A-4FC2-9C71-D25B8A8B13F1}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Singulink.Collections.Weak.Tests", "Tests\Singulink.Collections.Weak.Tests\Singulink.Collections.Weak.Tests.csproj", "{9534B47A-355D-4769-AA16-A5BE4511600C}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Debug|x64 = Debug|x64
- Debug|x86 = Debug|x86
- Release|Any CPU = Release|Any CPU
- Release|x64 = Release|x64
- Release|x86 = Release|x86
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {C802E214-0FE3-4115-A4E9-B08C8EE76759}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C802E214-0FE3-4115-A4E9-B08C8EE76759}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C802E214-0FE3-4115-A4E9-B08C8EE76759}.Debug|x64.ActiveCfg = Debug|Any CPU
- {C802E214-0FE3-4115-A4E9-B08C8EE76759}.Debug|x64.Build.0 = Debug|Any CPU
- {C802E214-0FE3-4115-A4E9-B08C8EE76759}.Debug|x86.ActiveCfg = Debug|Any CPU
- {C802E214-0FE3-4115-A4E9-B08C8EE76759}.Debug|x86.Build.0 = Debug|Any CPU
- {C802E214-0FE3-4115-A4E9-B08C8EE76759}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C802E214-0FE3-4115-A4E9-B08C8EE76759}.Release|Any CPU.Build.0 = Release|Any CPU
- {C802E214-0FE3-4115-A4E9-B08C8EE76759}.Release|x64.ActiveCfg = Release|Any CPU
- {C802E214-0FE3-4115-A4E9-B08C8EE76759}.Release|x64.Build.0 = Release|Any CPU
- {C802E214-0FE3-4115-A4E9-B08C8EE76759}.Release|x86.ActiveCfg = Release|Any CPU
- {C802E214-0FE3-4115-A4E9-B08C8EE76759}.Release|x86.Build.0 = Release|Any CPU
- {8EF2D844-0A43-47EF-B055-030D13F02088}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {8EF2D844-0A43-47EF-B055-030D13F02088}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {8EF2D844-0A43-47EF-B055-030D13F02088}.Debug|x64.ActiveCfg = Debug|Any CPU
- {8EF2D844-0A43-47EF-B055-030D13F02088}.Debug|x64.Build.0 = Debug|Any CPU
- {8EF2D844-0A43-47EF-B055-030D13F02088}.Debug|x86.ActiveCfg = Debug|Any CPU
- {8EF2D844-0A43-47EF-B055-030D13F02088}.Debug|x86.Build.0 = Debug|Any CPU
- {8EF2D844-0A43-47EF-B055-030D13F02088}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {8EF2D844-0A43-47EF-B055-030D13F02088}.Release|Any CPU.Build.0 = Release|Any CPU
- {8EF2D844-0A43-47EF-B055-030D13F02088}.Release|x64.ActiveCfg = Release|Any CPU
- {8EF2D844-0A43-47EF-B055-030D13F02088}.Release|x64.Build.0 = Release|Any CPU
- {8EF2D844-0A43-47EF-B055-030D13F02088}.Release|x86.ActiveCfg = Release|Any CPU
- {8EF2D844-0A43-47EF-B055-030D13F02088}.Release|x86.Build.0 = Release|Any CPU
- {37173589-E411-4806-A79C-153D9659FAE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {37173589-E411-4806-A79C-153D9659FAE7}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {37173589-E411-4806-A79C-153D9659FAE7}.Debug|x64.ActiveCfg = Debug|Any CPU
- {37173589-E411-4806-A79C-153D9659FAE7}.Debug|x64.Build.0 = Debug|Any CPU
- {37173589-E411-4806-A79C-153D9659FAE7}.Debug|x86.ActiveCfg = Debug|Any CPU
- {37173589-E411-4806-A79C-153D9659FAE7}.Debug|x86.Build.0 = Debug|Any CPU
- {37173589-E411-4806-A79C-153D9659FAE7}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {37173589-E411-4806-A79C-153D9659FAE7}.Release|Any CPU.Build.0 = Release|Any CPU
- {37173589-E411-4806-A79C-153D9659FAE7}.Release|x64.ActiveCfg = Release|Any CPU
- {37173589-E411-4806-A79C-153D9659FAE7}.Release|x64.Build.0 = Release|Any CPU
- {37173589-E411-4806-A79C-153D9659FAE7}.Release|x86.ActiveCfg = Release|Any CPU
- {37173589-E411-4806-A79C-153D9659FAE7}.Release|x86.Build.0 = Release|Any CPU
- {5EDDA4AB-D81A-4FC2-9C71-D25B8A8B13F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {5EDDA4AB-D81A-4FC2-9C71-D25B8A8B13F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {5EDDA4AB-D81A-4FC2-9C71-D25B8A8B13F1}.Debug|x64.ActiveCfg = Debug|Any CPU
- {5EDDA4AB-D81A-4FC2-9C71-D25B8A8B13F1}.Debug|x64.Build.0 = Debug|Any CPU
- {5EDDA4AB-D81A-4FC2-9C71-D25B8A8B13F1}.Debug|x86.ActiveCfg = Debug|Any CPU
- {5EDDA4AB-D81A-4FC2-9C71-D25B8A8B13F1}.Debug|x86.Build.0 = Debug|Any CPU
- {5EDDA4AB-D81A-4FC2-9C71-D25B8A8B13F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {5EDDA4AB-D81A-4FC2-9C71-D25B8A8B13F1}.Release|Any CPU.Build.0 = Release|Any CPU
- {5EDDA4AB-D81A-4FC2-9C71-D25B8A8B13F1}.Release|x64.ActiveCfg = Release|Any CPU
- {5EDDA4AB-D81A-4FC2-9C71-D25B8A8B13F1}.Release|x64.Build.0 = Release|Any CPU
- {5EDDA4AB-D81A-4FC2-9C71-D25B8A8B13F1}.Release|x86.ActiveCfg = Release|Any CPU
- {5EDDA4AB-D81A-4FC2-9C71-D25B8A8B13F1}.Release|x86.Build.0 = Release|Any CPU
- {9534B47A-355D-4769-AA16-A5BE4511600C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {9534B47A-355D-4769-AA16-A5BE4511600C}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {9534B47A-355D-4769-AA16-A5BE4511600C}.Debug|x64.ActiveCfg = Debug|Any CPU
- {9534B47A-355D-4769-AA16-A5BE4511600C}.Debug|x64.Build.0 = Debug|Any CPU
- {9534B47A-355D-4769-AA16-A5BE4511600C}.Debug|x86.ActiveCfg = Debug|Any CPU
- {9534B47A-355D-4769-AA16-A5BE4511600C}.Debug|x86.Build.0 = Debug|Any CPU
- {9534B47A-355D-4769-AA16-A5BE4511600C}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {9534B47A-355D-4769-AA16-A5BE4511600C}.Release|Any CPU.Build.0 = Release|Any CPU
- {9534B47A-355D-4769-AA16-A5BE4511600C}.Release|x64.ActiveCfg = Release|Any CPU
- {9534B47A-355D-4769-AA16-A5BE4511600C}.Release|x64.Build.0 = Release|Any CPU
- {9534B47A-355D-4769-AA16-A5BE4511600C}.Release|x86.ActiveCfg = Release|Any CPU
- {9534B47A-355D-4769-AA16-A5BE4511600C}.Release|x86.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(NestedProjects) = preSolution
- {37173589-E411-4806-A79C-153D9659FAE7} = {B95CC845-E2D3-4EF5-B7DF-FC902D58FF4C}
- {465CA120-1038-480C-893C-AE32B93017A3} = {B95CC845-E2D3-4EF5-B7DF-FC902D58FF4C}
- {9534B47A-355D-4769-AA16-A5BE4511600C} = {B95CC845-E2D3-4EF5-B7DF-FC902D58FF4C}
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {62EF1A76-6B44-49F1-A73B-520A855AEEBA}
- EndGlobalSection
-EndGlobal
diff --git a/Source/Singulink.Collections/FodyWeavers.xml b/Source/Singulink.Collections/FodyWeavers.xml
deleted file mode 100644
index a848a9d..0000000
--- a/Source/Singulink.Collections/FodyWeavers.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/Source/Singulink.Collections/HashSetDictionary.cs b/Source/Singulink.Collections/HashSetDictionary.cs
index b931d3d..25ed542 100644
--- a/Source/Singulink.Collections/HashSetDictionary.cs
+++ b/Source/Singulink.Collections/HashSetDictionary.cs
@@ -197,7 +197,7 @@ public bool ContainsValue(TValue value)
/// Ensures that the dictionary can hold up to a specified number of key/value set pairs without any further expansion of its backing storage.
///
/// The number of key/value set pairs.
- /// The currect capacity of the dictionary.
+ /// The current capacity of the dictionary.
/// Capacity specified is less than 0.
public int EnsureCapacity(int capacity)
{
diff --git a/Source/Singulink.Collections.Abstractions/ICollectionDictionary.cs b/Source/Singulink.Collections/ICollectionDictionary.cs
similarity index 100%
rename from Source/Singulink.Collections.Abstractions/ICollectionDictionary.cs
rename to Source/Singulink.Collections/ICollectionDictionary.cs
diff --git a/Source/Singulink.Collections.Abstractions/IListDictionary.cs b/Source/Singulink.Collections/IListDictionary.cs
similarity index 100%
rename from Source/Singulink.Collections.Abstractions/IListDictionary.cs
rename to Source/Singulink.Collections/IListDictionary.cs
diff --git a/Source/Singulink.Collections.Abstractions/IMap.cs b/Source/Singulink.Collections/IMap.cs
similarity index 100%
rename from Source/Singulink.Collections.Abstractions/IMap.cs
rename to Source/Singulink.Collections/IMap.cs
diff --git a/Source/Singulink.Collections.Abstractions/IReadOnlyCollectionDictionary.cs b/Source/Singulink.Collections/IReadOnlyCollectionDictionary.cs
similarity index 100%
rename from Source/Singulink.Collections.Abstractions/IReadOnlyCollectionDictionary.cs
rename to Source/Singulink.Collections/IReadOnlyCollectionDictionary.cs
diff --git a/Source/Singulink.Collections.Abstractions/IReadOnlyCollectionProvider.cs b/Source/Singulink.Collections/IReadOnlyCollectionProvider.cs
similarity index 82%
rename from Source/Singulink.Collections.Abstractions/IReadOnlyCollectionProvider.cs
rename to Source/Singulink.Collections/IReadOnlyCollectionProvider.cs
index bb70775..085eea2 100644
--- a/Source/Singulink.Collections.Abstractions/IReadOnlyCollectionProvider.cs
+++ b/Source/Singulink.Collections/IReadOnlyCollectionProvider.cs
@@ -1,8 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace Singulink.Collections;
+namespace Singulink.Collections;
///
/// Allows a collection to return a read-only wrapper. This interface should be implemented by the value collections in collection dictionaries to
diff --git a/Source/Singulink.Collections.Abstractions/IReadOnlyListDictionary.cs b/Source/Singulink.Collections/IReadOnlyListDictionary.cs
similarity index 100%
rename from Source/Singulink.Collections.Abstractions/IReadOnlyListDictionary.cs
rename to Source/Singulink.Collections/IReadOnlyListDictionary.cs
diff --git a/Source/Singulink.Collections.Abstractions/IReadOnlyMap.cs b/Source/Singulink.Collections/IReadOnlyMap.cs
similarity index 100%
rename from Source/Singulink.Collections.Abstractions/IReadOnlyMap.cs
rename to Source/Singulink.Collections/IReadOnlyMap.cs
diff --git a/Source/Singulink.Collections.Abstractions/IReadOnlySet.cs b/Source/Singulink.Collections/IReadOnlySet.cs
similarity index 100%
rename from Source/Singulink.Collections.Abstractions/IReadOnlySet.cs
rename to Source/Singulink.Collections/IReadOnlySet.cs
diff --git a/Source/Singulink.Collections.Abstractions/IReadOnlySetDictionary.cs b/Source/Singulink.Collections/IReadOnlySetDictionary.cs
similarity index 100%
rename from Source/Singulink.Collections.Abstractions/IReadOnlySetDictionary.cs
rename to Source/Singulink.Collections/IReadOnlySetDictionary.cs
diff --git a/Source/Singulink.Collections.Abstractions/ISetDictionary.cs b/Source/Singulink.Collections/ISetDictionary.cs
similarity index 100%
rename from Source/Singulink.Collections.Abstractions/ISetDictionary.cs
rename to Source/Singulink.Collections/ISetDictionary.cs
diff --git a/Source/Singulink.Collections/Polyfills/CollectionExtensions.cs b/Source/Singulink.Collections/Polyfills/CollectionExtensions.cs
index 809879b..2cdbd8d 100644
--- a/Source/Singulink.Collections/Polyfills/CollectionExtensions.cs
+++ b/Source/Singulink.Collections/Polyfills/CollectionExtensions.cs
@@ -1,12 +1,10 @@
-using System.Diagnostics.CodeAnalysis;
-
-namespace Singulink.Collections;
+namespace Singulink.Collections;
#if NETSTANDARD2_0
internal static class CollectionExtensions
{
- public static bool Remove(this Dictionary dictionary, TKey key, [MaybeNullWhen(false)] out TValue value) where TKey : notnull
+ public static bool Remove(this Dictionary dictionary, TKey key, out TValue value) where TKey : notnull
{
if (!dictionary.TryGetValue(key, out value))
return false;
diff --git a/Source/Singulink.Collections/Singulink.Collections.csproj b/Source/Singulink.Collections/Singulink.Collections.csproj
index a46f954..521a6e8 100644
--- a/Source/Singulink.Collections/Singulink.Collections.csproj
+++ b/Source/Singulink.Collections/Singulink.Collections.csproj
@@ -1,13 +1,10 @@
netstandard2.0;netstandard2.1;net6.0
- CA1034
+ $(NoWarn);CA1034
+
2.0
- Singulink
- MIT
- © Singulink. All rights reserved.
- https://github.com/Singulink/Singulink.Collections
- Singulink Icon 128x128.png
+ List; Dictionary; HashSet; Set
Widely useful highly optimized collections that are missing from the .NET BCL.
@@ -18,32 +15,14 @@
Singulink.Collections.ReadOnlyList<T>
Singulink.Collections.ReadOnlyHashSet<T>
+ https://github.com/Singulink/Singulink.Collections
+
true
key.snk
true
- List; Dictionary; HashSet; Set
- e9d5dddc-d4fb-4a06-84bd-190ee6180360
-
- true
- true
- true
- true
-
-
-
-
-
-
-
-
- True
-
-
-
-
-
-
+
+
diff --git a/Source/Singulink.Collections/Utilities/CollectionCopy.cs b/Source/Singulink.Collections/Utilities/CollectionCopy.cs
index a6070dc..f335c20 100644
--- a/Source/Singulink.Collections/Utilities/CollectionCopy.cs
+++ b/Source/Singulink.Collections/Utilities/CollectionCopy.cs
@@ -1,7 +1,4 @@
-using System;
-using System.Collections.Generic;
-
-namespace Singulink.Collections.Utilities;
+namespace Singulink.Collections.Utilities;
internal static class CollectionCopy
{
diff --git a/Source/Singulink.Collections/Utilities/Throw.cs b/Source/Singulink.Collections/Utilities/Throw.cs
index 19ab542..ce348c8 100644
--- a/Source/Singulink.Collections/Utilities/Throw.cs
+++ b/Source/Singulink.Collections/Utilities/Throw.cs
@@ -1,6 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.CodeAnalysis;
namespace Singulink.Collections.Utilities;
diff --git a/Source/Tests/Directory.Build.props b/Source/Tests/Directory.Build.props
deleted file mode 100644
index 28dadc6..0000000
--- a/Source/Tests/Directory.Build.props
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
- CS1591
- false
-
-
\ No newline at end of file
diff --git a/Source/Tests/Singulink.Collections.Tests/FodyWeavers.xml b/Source/Tests/Singulink.Collections.Tests/FodyWeavers.xml
deleted file mode 100644
index a848a9d..0000000
--- a/Source/Tests/Singulink.Collections.Tests/FodyWeavers.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/Source/Tests/Singulink.Collections.Tests/FodyWeavers.xsd b/Source/Tests/Singulink.Collections.Tests/FodyWeavers.xsd
deleted file mode 100644
index 0ceaa88..0000000
--- a/Source/Tests/Singulink.Collections.Tests/FodyWeavers.xsd
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-
-
-
-
-
-
-
- Determines whether outputs (return values and out/ref parameters) have null checks injected.
-
-
-
-
- Determines whether non-public members have null checks injected or if only public entry points are checked.
-
-
-
-
-
-
-
- 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
-
-
-
-
- A comma-separated list of error codes that can be safely ignored in assembly verification.
-
-
-
-
- 'false' to turn off automatic generation of the XML Schema file.
-
-
-
-
-
\ No newline at end of file
diff --git a/Source/Tests/Singulink.Collections.Tests/Singulink.Collections.Tests.csproj b/Source/Tests/Singulink.Collections.Tests/Singulink.Collections.Tests.csproj
deleted file mode 100644
index ad08681..0000000
--- a/Source/Tests/Singulink.Collections.Tests/Singulink.Collections.Tests.csproj
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
- net48;net6.0
-
-
-
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Source/Tests/Singulink.Collections.Weak.Tests/FodyWeavers.xml b/Source/Tests/Singulink.Collections.Weak.Tests/FodyWeavers.xml
deleted file mode 100644
index a848a9d..0000000
--- a/Source/Tests/Singulink.Collections.Weak.Tests/FodyWeavers.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/Source/Tests/Singulink.Collections.Weak.Tests/FodyWeavers.xsd b/Source/Tests/Singulink.Collections.Weak.Tests/FodyWeavers.xsd
deleted file mode 100644
index 0ceaa88..0000000
--- a/Source/Tests/Singulink.Collections.Weak.Tests/FodyWeavers.xsd
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-
-
-
-
-
-
-
- Determines whether outputs (return values and out/ref parameters) have null checks injected.
-
-
-
-
- Determines whether non-public members have null checks injected or if only public entry points are checked.
-
-
-
-
-
-
-
- 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
-
-
-
-
- A comma-separated list of error codes that can be safely ignored in assembly verification.
-
-
-
-
- 'false' to turn off automatic generation of the XML Schema file.
-
-
-
-
-
\ No newline at end of file
diff --git a/Source/Tests/Singulink.Collections.Weak.Tests/GCTests.cs b/Source/Tests/Singulink.Collections.Weak.Tests/GCTests.cs
deleted file mode 100644
index 6d34ddd..0000000
--- a/Source/Tests/Singulink.Collections.Weak.Tests/GCTests.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-
-namespace Singulink.Collections.Weak.Tests
-{
- [TestClass]
- public class GCTests
- {
- [TestMethod]
- public void EnterNoGCRegionAndCollect()
- {
- var weakRef = Helpers.GetWeakRef();
-
- using (NoGCRegion.Enter(1000)) { }
-
- Helpers.CollectAndWait();
- Assert.IsFalse(weakRef.TryGetTarget(out _));
- }
- }
-}
\ No newline at end of file
diff --git a/Source/Tests/Singulink.Collections.Weak.Tests/Helpers.cs b/Source/Tests/Singulink.Collections.Weak.Tests/Helpers.cs
deleted file mode 100644
index 8f18a2e..0000000
--- a/Source/Tests/Singulink.Collections.Weak.Tests/Helpers.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System.Runtime.CompilerServices;
-
-namespace Singulink.Collections.Weak.Tests
-{
- internal static class Helpers
- {
- [MethodImpl(MethodImplOptions.NoInlining)]
- public static WeakReference