diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 812bf15..bf8da1d 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -6,7 +6,7 @@ on:
build_version_base:
description: 'Build version number X.Y.Z'
required: false
- default: 0.9.0
+ default: 0.10.0
jobs:
@@ -37,7 +37,7 @@ jobs:
- name: Install .NET
uses: actions/setup-dotnet@v4
with:
- dotnet-version: 8.0.x
+ dotnet-version: 9.0.x
# Create the NuGet packages in the folder from the environment variable NuGetDirectory
- name: Build NuGet packages
diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml
index 72be51d..a3c13d9 100644
--- a/.github/workflows/verify.yml
+++ b/.github/workflows/verify.yml
@@ -22,7 +22,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
- dotnet-version: '8.0.x'
+ dotnet-version: '9.0.x'
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
@@ -81,7 +81,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
- dotnet-version: '8.0.x'
+ dotnet-version: '9.0.x'
- name: Install dependencies
run: dotnet restore CommonNet.Extensions.Tests/CommonNet.Extensions.Tests.csproj
- name: Build Tests
diff --git a/CommonNet.DependencyInjection/CommonNet.DependencyInjection.csproj b/CommonNet.DependencyInjection/CommonNet.DependencyInjection.csproj
index 3362af9..27b9762 100644
--- a/CommonNet.DependencyInjection/CommonNet.DependencyInjection.csproj
+++ b/CommonNet.DependencyInjection/CommonNet.DependencyInjection.csproj
@@ -2,7 +2,7 @@
true
- Various Dependency Injectionextensions.
+ Various Dependency Injection extensions.
MIT
diff --git a/CommonNet.DependencyInjection/Microsoft.Extensions.DependencyInjection/ServiceCollectionExtensions.AddSingleton.cs b/CommonNet.DependencyInjection/Microsoft.Extensions.DependencyInjection/ServiceCollectionExtensions.AddSingleton.cs
index d248075..232f74a 100644
--- a/CommonNet.DependencyInjection/Microsoft.Extensions.DependencyInjection/ServiceCollectionExtensions.AddSingleton.cs
+++ b/CommonNet.DependencyInjection/Microsoft.Extensions.DependencyInjection/ServiceCollectionExtensions.AddSingleton.cs
@@ -210,7 +210,6 @@ public static IServiceCollection AddSingleton(sp => sp.GetRequiredService)
.AddSingleton(implementationFactory)
.AddSingleton(sp => (sp.GetRequiredService() as TService2)!);
}
diff --git a/CommonNet.Extensions.Tests/CommonNet.DependencyInjection/ServiceCollectionExtensionsTests.cs b/CommonNet.Extensions.Tests/CommonNet.DependencyInjection/ServiceCollectionExtensionsTests.cs
index 1431454..9f69240 100644
--- a/CommonNet.Extensions.Tests/CommonNet.DependencyInjection/ServiceCollectionExtensionsTests.cs
+++ b/CommonNet.Extensions.Tests/CommonNet.DependencyInjection/ServiceCollectionExtensionsTests.cs
@@ -69,6 +69,47 @@ public void AddSingleton_ShouldSucceed()
bif2.Should().NotBe(bif1);
}
+ [Fact]
+ public void AddSingletonWithFactory_ShouldSucceed()
+ {
+ var services = new ServiceCollection();
+
+ services
+ .AddSingletonIf(true, sp => new A4_1())
+ .AddSingletonIf(true, sp => new B4_2());
+
+ using var sp = services.BuildServiceProvider();
+
+ var iif4 = sp.GetRequiredService();
+ iif4.Should().NotBeNull();
+ var iif3 = sp.GetRequiredService();
+ iif3.Should().NotBeNull();
+ var iif2 = sp.GetRequiredService();
+ iif2.Should().NotBeNull();
+ var bif1 = sp.GetRequiredService();
+ bif1.Should().NotBeNull();
+
+ iif4.Should().Be(iif3);
+ iif3.Should().Be(iif2);
+ iif2.Should().Be(bif1);
+
+
+ var bif2 = sp.GetRequiredService();
+ bif2.Should().NotBeNull();
+ var bif3 = sp.GetRequiredService();
+ bif3.Should().NotBeNull();
+ var bif4 = sp.GetRequiredService();
+ bif4.Should().NotBeNull();
+ var bif5 = sp.GetRequiredService();
+ bif5.Should().NotBeNull();
+
+ bif2.Should().Be(bif3);
+ bif3.Should().Be(bif4);
+ bif4.Should().Be(bif5);
+
+ bif2.Should().NotBe(bif1);
+ }
+
[Fact]
public void AddSingletonFact_ShouldSucceed()
{
diff --git a/CommonNet.Extensions.Tests/CommonNet.Extensions.Tests.csproj b/CommonNet.Extensions.Tests/CommonNet.Extensions.Tests.csproj
index 4619fe0..14cec64 100644
--- a/CommonNet.Extensions.Tests/CommonNet.Extensions.Tests.csproj
+++ b/CommonNet.Extensions.Tests/CommonNet.Extensions.Tests.csproj
@@ -1,8 +1,8 @@
- net6.0;net7.0;net8.0
- net8.0;net7.0;net6.0;net48
+ net8.0;net9.0
+ net8.0;net9.0;net48
true
@@ -24,8 +24,8 @@
all
runtime; build; native; contentfiles; analyzers
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/CommonNet.Extensions.Tests/CommonNet.Extensions/WaitHandleExtensionsTests.cs b/CommonNet.Extensions.Tests/CommonNet.Extensions/WaitHandleExtensionsTests.cs
index 67e676c..2bed7b7 100644
--- a/CommonNet.Extensions.Tests/CommonNet.Extensions/WaitHandleExtensionsTests.cs
+++ b/CommonNet.Extensions.Tests/CommonNet.Extensions/WaitHandleExtensionsTests.cs
@@ -63,6 +63,74 @@ public async Task AsTask_WithTimeout_ShouldCancel_WhenTimeoutIsReached()
task.IsCanceled.Should().BeTrue();
}
+
+
+#if NET8_0_OR_GREATER
+
+ [Fact]
+ public async Task WaitAsync_WaitHandle_Signaled_CompletesTask()
+ {
+ using var manualEvent = new ManualResetEvent(false);
+ WaitHandle waitHandle = manualEvent;
+ var cancellationToken = CancellationToken.None;
+
+ var task = waitHandle.WaitAsync(cancellationToken);
+ manualEvent.Set();
+
+ await task.Awaiting(t => t).Should().NotThrowAsync();
+ }
+
+ [Fact]
+ public async Task WaitAsync_WaitHandle_Canceled_ThrowsTaskCanceledException()
+ {
+ using var manualEvent = new ManualResetEvent(false);
+ WaitHandle waitHandle = manualEvent;
+ var cts = new CancellationTokenSource();
+
+ var task = waitHandle.WaitAsync(cts.Token);
+ cts.Cancel();
+
+ await task.Awaiting(t => t).Should().ThrowAsync();
+ }
+
+ [Fact]
+ public async Task WaitAsync_ManualResetEventSlim_Signaled_CompletesTask()
+ {
+
+ using var manualResetEvent = new ManualResetEventSlim(false);
+ var cancellationToken = CancellationToken.None;
+
+ var task = manualResetEvent.WaitAsync(cancellationToken);
+ manualResetEvent.Set();
+
+ await task.Awaiting(t => t).Should().NotThrowAsync();
+ }
+
+ [Fact]
+ public async Task WaitAsync_ManualResetEventSlim_Canceled_ThrowsTaskCanceledException()
+ {
+ // Arrange
+ using var manualResetEvent = new ManualResetEventSlim(false);
+ var cts = new CancellationTokenSource();
+
+ var task = manualResetEvent.WaitAsync(cts.Token);
+ cts.Cancel();
+
+ await task.Awaiting(t => t).Should().ThrowAsync();
+ }
+
+ [Fact]
+ public void WaitAsync_WaitHandle_Null_ThrowsArgumentNullException()
+ {
+ WaitHandle waitHandle = null!;
+ var cancellationToken = CancellationToken.None;
+
+ var func = () => waitHandle.WaitAsync(cancellationToken);
+ func.Should().ThrowAsync();
+ }
+
+#endif
+
}
#pragma warning restore xUnit1031
diff --git a/CommonNet.Extensions/System.Collections.Generic/DictionaryExtensions.cs b/CommonNet.Extensions/System.Collections.Generic/DictionaryExtensions.cs
index acfc326..3c91ba9 100644
--- a/CommonNet.Extensions/System.Collections.Generic/DictionaryExtensions.cs
+++ b/CommonNet.Extensions/System.Collections.Generic/DictionaryExtensions.cs
@@ -1,5 +1,6 @@
using CommunityToolkit.Diagnostics;
using System.ComponentModel;
+using System.Runtime.InteropServices;
namespace System.Collections.Generic;
@@ -25,14 +26,22 @@ Func valueFactory
where TKey : notnull
{
Guard.IsNotNull(self);
- Guard.IsNotNull(key);
Guard.IsNotNull(valueFactory);
+#if NET8_0_OR_GREATER
+ ref var value = ref CollectionsMarshal.GetValueRefOrAddDefault(self, key, out var exists);
+ if (exists)
+ {
+ return value!;
+ }
+ value = valueFactory(key);
+#else
if (!self.TryGetValue(key, out var value))
{
value = valueFactory(key);
self.Add(key, value);
}
+#endif
return value;
}
@@ -51,13 +60,21 @@ TValue newValue
where TKey : notnull
{
Guard.IsNotNull(self);
- Guard.IsNotNull(key);
+#if NET8_0_OR_GREATER
+ ref var value = ref CollectionsMarshal.GetValueRefOrAddDefault(self, key, out var exists);
+ if (exists)
+ {
+ return value!;
+ }
+ value = newValue;
+#else
if (!self.TryGetValue(key, out var value))
{
value = newValue;
self.Add(key, value);
}
+#endif
return value;
}
@@ -100,21 +117,32 @@ Func updateValueFactory
where TKey : notnull
{
Guard.IsNotNull(self);
- Guard.IsNotNull(key);
Guard.IsNotNull(updateValueFactory);
- TValue newValue;
+#if NET8_0_OR_GREATER
+ ref var value = ref CollectionsMarshal.GetValueRefOrAddDefault(self, key, out var exists);
+ if (exists)
+ {
+ value = updateValueFactory(key, value!);
+ }
+ else
+ {
+ value = addValue;
+ }
+#else
+ TValue value;
if (self.TryGetValue(key, out var oldValue))
{
- newValue = updateValueFactory(key, oldValue);
- self[key] = newValue;
+ value = updateValueFactory(key, oldValue);
+ self[key] = value;
}
else
{
- newValue = addValue;
- self.Add(key, newValue);
+ value = addValue;
+ self.Add(key, value);
}
- return newValue;
+#endif
+ return value;
}
///
@@ -135,22 +163,33 @@ Func updateValueFactory
where TKey : notnull
{
Guard.IsNotNull(self);
- Guard.IsNotNull(key);
Guard.IsNotNull(addValueFactory);
Guard.IsNotNull(updateValueFactory);
- TValue newValue;
+#if NET8_0_OR_GREATER
+ ref var value = ref CollectionsMarshal.GetValueRefOrAddDefault(self, key, out var exists);
+ if (exists)
+ {
+ value = updateValueFactory(key, value!);
+ }
+ else
+ {
+ value = addValueFactory(key);
+ }
+#else
+ TValue value;
if (self.TryGetValue(key, out var oldValue))
{
- newValue = updateValueFactory(key, oldValue);
- self[key] = newValue;
+ value = updateValueFactory(key, oldValue);
+ self[key] = value;
}
else
{
- newValue = addValueFactory(key);
- self.Add(key, newValue);
+ value = addValueFactory(key);
+ self.Add(key, value);
}
- return newValue;
+#endif
+ return value;
}
///
diff --git a/CommonNet.Extensions/System.Reflection/EmbeddedResourceStreamReader.cs b/CommonNet.Extensions/System.Reflection/EmbeddedResourceStreamReader.cs
index c7e80b4..847fa31 100644
--- a/CommonNet.Extensions/System.Reflection/EmbeddedResourceStreamReader.cs
+++ b/CommonNet.Extensions/System.Reflection/EmbeddedResourceStreamReader.cs
@@ -50,7 +50,7 @@ public static byte[] ReadEmbeddedResourceData(this Assembly assembly, string res
Guard.IsNotNull(stream, nameof(resourceName));
var data = new byte[stream.Length];
- stream.Read(data, 0, data.Length);
+ _ = stream.Read(data, 0, data.Length);
return data;
}
diff --git a/CommonNet.Extensions/System.Threading.Tasks/WaitHandleExtensions.cs b/CommonNet.Extensions/System.Threading.Tasks/WaitHandleExtensions.cs
index 3b27425..b295df3 100644
--- a/CommonNet.Extensions/System.Threading.Tasks/WaitHandleExtensions.cs
+++ b/CommonNet.Extensions/System.Threading.Tasks/WaitHandleExtensions.cs
@@ -50,4 +50,62 @@ public static Task AsTask(this WaitHandle handle, TimeSpan timeout)
tcs.Task.ContinueWith((_, state) => ((RegisteredWaitHandle)state!).Unregister(null), registration, TaskScheduler.Default);
return tcs.Task;
}
+
+#if NET8_0_OR_GREATER
+
+ ///
+ /// Asynchronously waits for a to be signaled, with support for cancellation.
+ ///
+ /// The to wait for.
+ /// A to observe while waiting for the handle to be signaled.
+ /// A that completes when the is signaled or the is canceled.
+ /// Thrown if is null.
+ ///
+ /// This method registers a callback with the thread pool to wait for the to be signaled.
+ /// If the is canceled before the handle is signaled, the returned will be canceled.
+ ///
+ public static Task WaitAsync(this WaitHandle waitHandle, CancellationToken cancellationToken = default)
+ {
+ Guard.IsNotNull(waitHandle);
+
+ CancellationTokenRegistration cancellationRegistration = default;
+
+ var tcs = new TaskCompletionSource();
+ var handle = ThreadPool.RegisterWaitForSingleObject(
+ waitObject: waitHandle,
+ callBack: (o, timeout) =>
+ {
+ _ = cancellationRegistration.Unregister();
+ _ = tcs.TrySetResult();
+ },
+ state: null,
+ timeout: Timeout.InfiniteTimeSpan,
+ executeOnlyOnce: true);
+
+ if (cancellationToken.CanBeCanceled)
+ {
+ cancellationRegistration = cancellationToken.Register(() =>
+ {
+ _ = handle.Unregister(waitHandle);
+ _ = tcs.TrySetCanceled(cancellationToken);
+ });
+ }
+
+ return tcs.Task;
+ }
+
+ ///
+ /// Asynchronously waits for a to be set, with support for cancellation.
+ ///
+ /// The to wait for.
+ /// A to observe while waiting for the event to be set.
+ /// A that completes when the is set or the is canceled.
+ /// Thrown if is null.
+ ///
+ /// This method calls with the of the .
+ ///
+ public static Task WaitAsync(this ManualResetEventSlim manualResetEvent, CancellationToken cancellationToken = default)
+ => WaitAsync(manualResetEvent.WaitHandle, cancellationToken);
+
+#endif
}
diff --git a/Directory.Build.props b/Directory.Build.props
index b30fb7f..7211f47 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,7 +1,7 @@
- netstandard2.0;net6.0;net7.0;net8.0
+ netstandard2.0;net8.0;net9.0
Debug;Release
latest
enable
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 4800d04..f87e60e 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -1,23 +1,26 @@
-
-
+
+
-
-
-
-
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
-
-
+
+
\ No newline at end of file