diff --git a/docs/CodeDoc/Atc/Atc.Helpers.md b/docs/CodeDoc/Atc/Atc.Helpers.md
index f5914457..0fed788a 100644
--- a/docs/CodeDoc/Atc/Atc.Helpers.md
+++ b/docs/CodeDoc/Atc/Atc.Helpers.md
@@ -974,6 +974,47 @@ Enumeration Helper: EnumHelper.
> `ignoreCase` - if set to true [ignore case].
>
>Returns: If parsed successfully and defined as a valid enum value, the enum value is returned. Otherwise the default value is returned.
+#### GetIndividualValues
+>```csharp
+>IList GetIndividualValues(bool includeDefault = True)
+>```
+>Summary: Retrieves individual flag values from a enum.
+>
+>Parameters:
+> `includeDefault` - Includes the default '0' value if true.
+>
+>Returns: A list of individual values.
+#### GetIndividualValuesByCombinedValueFromFlagEnum
+>```csharp
+>IList GetIndividualValuesByCombinedValueFromFlagEnum(T combinedValue, bool includeDefault = True)
+>```
+>Summary: Extracts and returns individual flags from a combined flag value.
+>
+>Parameters:
+> `combinedValue` - The aggregate value of flags.
+> `includeDefault` - Includes the default '0' value if true.
+>
+>Returns: A list of matching individual flags.
+#### GetIndividualValuesFromEnum
+>```csharp
+>IList GetIndividualValuesFromEnum(bool includeDefault = True)
+>```
+>Summary: Retrieves values from a regular (non-flag) enum.
+>
+>Parameters:
+> `includeDefault` - Includes the default '0' value if true.
+>
+>Returns: A list of enum values.
+#### GetIndividualValuesFromFlagEnum
+>```csharp
+>IList GetIndividualValuesFromFlagEnum(bool includeDefault = True)
+>```
+>Summary: Retrieves individual flag values from a flag-based enum.
+>
+>Parameters:
+> `includeDefault` - Includes the default '0' value if true.
+>
+>Returns: A list of individual flag values.
#### GetName
>```csharp
>string GetName(Enum enumeration)
@@ -1971,6 +2012,22 @@ TaskHelper.
> `taskToRun` - The task to run.
> `timeout` - The timeout.
> `cancellationToken` - The cancellation token.
+#### FireAndForget
+>```csharp
+>void FireAndForget(Action action)
+>```
+>Summary: Executes the provided action on a background thread, ignoring its completion status. This method is intended for fire-and-forget scenarios where the action is non-critical and does not need to be awaited or monitored.
+>
+>Parameters:
+> `action` - The action to execute asynchronously.
+#### FireAndForget
+>```csharp
+>void FireAndForget(Task task)
+>```
+>Summary: Executes the provided action on a background thread, ignoring its completion status. This method is intended for fire-and-forget scenarios where the action is non-critical and does not need to be awaited or monitored.
+>
+>Parameters:
+> `action` - The action to execute asynchronously.
#### RunSync
>```csharp
>void RunSync(Func func)
diff --git a/docs/CodeDoc/Atc/IndexExtended.md b/docs/CodeDoc/Atc/IndexExtended.md
index 2d4e4651..7e9b94e7 100644
--- a/docs/CodeDoc/Atc/IndexExtended.md
+++ b/docs/CodeDoc/Atc/IndexExtended.md
@@ -4502,6 +4502,10 @@
- ConvertEnumToDictionaryWithStringKey(Type enumType, DropDownFirstItemType dropDownFirstItemType = None, bool useDescriptionAttribute = False, bool includeDefault = True, SortDirectionType sortDirectionType = None, bool byFlagIncludeBase = True, bool byFlagIncludeCombined = True)
- GetDescription(Enum enumeration)
- GetEnumValue(string value, bool ignoreCase = True)
+ - GetIndividualValues(bool includeDefault = True)
+ - GetIndividualValuesByCombinedValueFromFlagEnum(T combinedValue, bool includeDefault = True)
+ - GetIndividualValuesFromEnum(bool includeDefault = True)
+ - GetIndividualValuesFromFlagEnum(bool includeDefault = True)
- GetName(Enum enumeration)
- GetValueFromDescription(string description)
- [FileHelper](Atc.Helpers.md#filehelper)
@@ -4663,6 +4667,8 @@
- [TaskHelper](Atc.Helpers.md#taskhelper)
- Static Methods
- Execute(Func<CancellationToken, Task<TResult>> taskToRun, TimeSpan timeout, CancellationToken cancellationToken = null)
+ - FireAndForget(Action action)
+ - FireAndForget(Task task)
- RunSync(Func<Task> func)
- RunSync(Func<Task<TResult>> func)
- WhenAll(IEnumerable<Task> tasks)
@@ -5146,6 +5152,7 @@
- [SwitchCaseDefaultException](System.md#switchcasedefaultexception)
- [TaskExtensions](System.md#taskextensions)
- Static Methods
+ - Forget(this Task task)
- StartAndWaitAllThrottled(this IEnumerable<Task> tasksToRun, int maxTasksToRunInParallel, CancellationToken cancellationToken = null)
- StartAndWaitAllThrottled(this IEnumerable<Task> tasksToRun, int maxTasksToRunInParallel, int timeoutInMilliseconds, CancellationToken cancellationToken = null)
- [TcpException](System.md#tcpexception)
diff --git a/docs/CodeDoc/Atc/System.md b/docs/CodeDoc/Atc/System.md
index 547e9ac3..7b7b6e0d 100644
--- a/docs/CodeDoc/Atc/System.md
+++ b/docs/CodeDoc/Atc/System.md
@@ -2287,6 +2287,14 @@ Extensions for the `System.Threading.Tasks.Task` class.
### Static Methods
+#### Forget
+>```csharp
+>void Forget(this Task task)
+>```
+>Summary: Marks the provided task as 'forgotten', meaning its completion is intentionally unobserved. This method is used to explicitly denote that a task's result or exception is to be ignored. It should be used with caution, primarily in fire-and-forget scenarios where task exceptions are handled separately.
+>
+>Parameters:
+> `task` - The task to be forgotten.
#### StartAndWaitAllThrottled
>```csharp
>void StartAndWaitAllThrottled(this IEnumerable tasksToRun, int maxTasksToRunInParallel, CancellationToken cancellationToken = null)
diff --git a/src/Atc/Extensions/TaskExtensions.cs b/src/Atc/Extensions/TaskExtensions.cs
index cc084627..6e3bca23 100644
--- a/src/Atc/Extensions/TaskExtensions.cs
+++ b/src/Atc/Extensions/TaskExtensions.cs
@@ -69,4 +69,39 @@ public static void StartAndWaitAllThrottled(this IEnumerable tasksToRun, i
// before some Tasks have had their "post" action completed, which references the throttler, resulting in an exception due to accessing a disposed object.
Task.WaitAll(postTaskTasks.ToArray(), cancellationToken);
}
+
+ ///
+ /// Marks the provided task as 'forgotten', meaning its completion is intentionally unobserved.
+ /// This method is used to explicitly denote that a task's result or exception is to be ignored.
+ /// It should be used with caution, primarily in fire-and-forget scenarios where task exceptions are handled separately.
+ ///
+ /// The task to be forgotten.
+ /// Thrown if the task is null.
+ public static void Forget(
+ this Task task)
+ {
+ if (task is null)
+ {
+ throw new ArgumentNullException(nameof(task));
+ }
+
+ if (!task.IsCompleted || task.IsFaulted)
+ {
+ _ = ForgetAwaited(task);
+ }
+ }
+
+ [SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "OK.")]
+ private static async Task ForgetAwaited(
+ Task task)
+ {
+ try
+ {
+ await task.ConfigureAwait(false);
+ }
+ catch
+ {
+ // Nothing to do here
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Atc/Helpers/Enums/EnumHelper.cs b/src/Atc/Helpers/Enums/EnumHelper.cs
index 2d3be796..9758cc18 100644
--- a/src/Atc/Helpers/Enums/EnumHelper.cs
+++ b/src/Atc/Helpers/Enums/EnumHelper.cs
@@ -1,5 +1,7 @@
// ReSharper disable UnreachableSwitchCaseDueToIntegerAnalysis
// ReSharper disable once CheckNamespace
+// ReSharper disable LoopCanBeConvertedToQuery
+// ReSharper disable ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
namespace Atc.Helpers;
///
@@ -398,7 +400,191 @@ public static Dictionary ConvertEnumToDictionaryWithStringKey(
return orderList.ToDictionary(x => x.Key, x => x.Value, StringComparer.Ordinal);
}
- internal static bool ShouldEnumValueBeSkipped(
+ ///
+ /// Retrieves individual flag values from a enum.
+ ///
+ /// The enum type.
+ /// Includes the default '0' value if true.
+ /// A list of individual values.
+ /// Thrown if T is not an enum.
+ public static IList GetIndividualValues(
+ bool includeDefault = true)
+ where T : Enum
+ {
+ if (!typeof(T).IsEnum)
+ {
+ throw new InvalidOperationException("The generic type parameter must be an enum.");
+ }
+
+ return typeof(T).GetCustomAttribute() is null
+ ? GetIndividualValuesFromEnum(includeDefault)
+ : GetIndividualValuesFromFlagEnum(includeDefault);
+ }
+
+ ///
+ /// Retrieves individual flag values from a flag-based enum.
+ ///
+ /// The enum type with [Flags] attribute.
+ /// Includes the default '0' value if true.
+ /// A list of individual flag values.
+ /// Thrown if T is not an enum with the [Flags] attribute.
+ public static IList GetIndividualValuesFromFlagEnum(
+ bool includeDefault = true)
+ where T : Enum
+ {
+ if (!typeof(T).IsEnum ||
+ typeof(T).GetCustomAttribute() is null)
+ {
+ throw new InvalidOperationException("The generic type parameter must be an enum with the [Flags] attribute.");
+ }
+
+ var allValues = Enum
+ .GetValues(typeof(T))
+ .Cast()
+ .ToList();
+
+ var individualValues = new List();
+ foreach (var value in allValues)
+ {
+ var valueAsLong = Convert.ToInt64(value, GlobalizationConstants.EnglishCultureInfo);
+ if (!includeDefault &&
+ valueAsLong == 0)
+ {
+ continue;
+ }
+
+ var bitCount = CountBitsForEnumValue(valueAsLong);
+ if (includeDefault)
+ {
+ if (bitCount is 0 or 1)
+ {
+ individualValues.Add(value);
+ }
+ }
+ else
+ {
+ if (bitCount == 1)
+ {
+ individualValues.Add(value);
+ }
+ }
+ }
+
+ return individualValues;
+ }
+
+ ///
+ /// Extracts and returns individual flags from a combined flag value.
+ ///
+ /// The enum type with [Flags] attribute.
+ /// The aggregate value of flags.
+ /// Includes the default '0' value if true.
+ /// A list of matching individual flags.
+ /// Thrown if T is not an enum with the [Flags] attribute or the combined value is invalid.
+ public static IList GetIndividualValuesByCombinedValueFromFlagEnum(
+ T combinedValue,
+ bool includeDefault = true)
+ where T : Enum
+ {
+ if (!typeof(T).IsEnum ||
+ typeof(T).GetCustomAttribute() is null)
+ {
+ throw new InvalidOperationException("The generic type parameter must be an enum with the [Flags] attribute.");
+ }
+
+ var combinedValueAsLong = Convert.ToInt64(combinedValue, GlobalizationConstants.EnglishCultureInfo);
+ if (CountBitsForEnumValue(combinedValueAsLong) == 1)
+ {
+ throw new InvalidOperationException("The combined value is invalid.");
+ }
+
+ var matchingValues = new List();
+ foreach (T value in Enum.GetValues(typeof(T)))
+ {
+ var valueAsLong = Convert.ToInt64(value, GlobalizationConstants.EnglishCultureInfo);
+ if (!includeDefault &&
+ valueAsLong == 0)
+ {
+ continue;
+ }
+
+ var bitCount = CountBitsForEnumValue(valueAsLong);
+ if ((combinedValueAsLong & valueAsLong) != valueAsLong)
+ {
+ continue;
+ }
+
+ if (includeDefault)
+ {
+ if (bitCount is 0 or 1)
+ {
+ matchingValues.Add(value);
+ }
+ }
+ else
+ {
+ if (bitCount == 1)
+ {
+ matchingValues.Add(value);
+ }
+ }
+ }
+
+ return matchingValues;
+ }
+
+ ///
+ /// Retrieves values from a regular (non-flag) enum.
+ ///
+ /// The enum type without the [Flags] attribute.
+ /// Includes the default '0' value if true.
+ /// A list of enum values.
+ /// Thrown if T is an enum with the [Flags] attribute.
+ public static IList GetIndividualValuesFromEnum(
+ bool includeDefault = true)
+ where T : Enum
+ {
+ if (!typeof(T).IsEnum ||
+ typeof(T).GetCustomAttribute() is not null)
+ {
+ throw new InvalidOperationException("The generic type parameter must be an enum without the [Flags] attribute.");
+ }
+
+ var allValues = Enum
+ .GetValues(typeof(T))
+ .Cast()
+ .ToList();
+
+ var individualValues = new List();
+ foreach (var value in allValues)
+ {
+ var valueAsLong = Convert.ToInt64(value, GlobalizationConstants.EnglishCultureInfo);
+ if (!includeDefault &&
+ valueAsLong == 0)
+ {
+ continue;
+ }
+
+ individualValues.Add(value);
+ }
+
+ return individualValues;
+ }
+
+ private static int CountBitsForEnumValue(
+ long value)
+ {
+ var count = 0;
+ while (value != 0)
+ {
+ count++;
+ value &= value - 1; // Clear the least significant bit set.
+ }
+
+ return count;
+ }
+
+ private static bool ShouldEnumValueBeSkipped(
object objEnumValue,
bool includeDefault,
bool hasFlagAttribute,
diff --git a/src/Atc/Helpers/TaskHelper.cs b/src/Atc/Helpers/TaskHelper.cs
index 682de86d..88e2f125 100644
--- a/src/Atc/Helpers/TaskHelper.cs
+++ b/src/Atc/Helpers/TaskHelper.cs
@@ -169,4 +169,41 @@ public static TResult RunSync(Func> func)
.GetAwaiter()
.GetResult();
}
+
+ ///
+ /// Executes the provided action on a background thread, ignoring its completion status.
+ /// This method is intended for fire-and-forget scenarios where the action is non-critical and does not need to be awaited or monitored.
+ ///
+ /// The action to execute asynchronously.
+ /// Thrown if the action is null.
+ [SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "OK.")]
+ public static void FireAndForget(
+ Action action)
+ {
+ if (action is null)
+ {
+ throw new ArgumentNullException(nameof(action));
+ }
+
+ Task.Run(action).Forget();
+ }
+
+ ///
+ /// Initiates the execution of a provided task and intentionally ignores its result or completion status.
+ /// This is a fire-and-forget utility method, used when the outcome of the task is not needed or is handled elsewhere.
+ /// It's primarily used for tasks where the result is not critical and does not need to be awaited or monitored.
+ ///
+ /// The task to execute and forget.
+ /// Thrown if the task is null.
+ [SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "OK.")]
+ public static void FireAndForget(
+ Task task)
+ {
+ if (task is null)
+ {
+ throw new ArgumentNullException(nameof(task));
+ }
+
+ task.Forget();
+ }
}
\ No newline at end of file
diff --git a/test/Atc.Tests/CodeComplianceTests.cs b/test/Atc.Tests/CodeComplianceTests.cs
index 4ec3e5d9..820bc628 100644
--- a/test/Atc.Tests/CodeComplianceTests.cs
+++ b/test/Atc.Tests/CodeComplianceTests.cs
@@ -40,6 +40,7 @@ public class CodeComplianceTests
// UnitTests are made, but CodeCompliance test cannot detect this
typeof(DynamicJson),
+ typeof(EnumHelper),
typeof(NumberHelper),
typeof(InternetBrowserHelper),
typeof(FileInfoExtensions),
diff --git a/test/Atc.Tests/Helpers/Enums/EnumHelperTests.cs b/test/Atc.Tests/Helpers/Enums/EnumHelperTests.cs
index 49670ba0..fffe8dc5 100644
--- a/test/Atc.Tests/Helpers/Enums/EnumHelperTests.cs
+++ b/test/Atc.Tests/Helpers/Enums/EnumHelperTests.cs
@@ -160,4 +160,67 @@ public void ConvertEnumToDictionaryWithStringKey(
// Assert
actual.Should().NotBeNull().And.HaveCount(expectedCount);
}
+
+ [Theory]
+ [InlineData(16, false)]
+ [InlineData(17, true)]
+ public void GetIndividualValues_CardinalDirectionType(
+ int expected,
+ bool includeDefault)
+ {
+ // Act
+ var actual = EnumHelper.GetIndividualValues(includeDefault);
+
+ // Assert
+ Assert.Equal(expected, actual.Count);
+ }
+
+ [Theory]
+ [InlineData(16, false)]
+ [InlineData(17, true)]
+ public void GetIndividualValuesFromFlagEnum_CardinalDirectionType(
+ int expected,
+ bool includeDefault)
+ {
+ // Act
+ var actual = EnumHelper.GetIndividualValuesFromFlagEnum(includeDefault);
+
+ // Assert
+ Assert.Equal(expected, actual.Count);
+ }
+
+ [Theory]
+ [InlineData(4, false, CardinalDirectionType.Simple)]
+ [InlineData(5, true, CardinalDirectionType.Simple)]
+ [InlineData(8, false, CardinalDirectionType.Medium)]
+ [InlineData(9, true, CardinalDirectionType.Medium)]
+ [InlineData(16, false, CardinalDirectionType.Advanced)]
+ [InlineData(17, true, CardinalDirectionType.Advanced)]
+ public void GetIndividualValuesByCombinedValueFromFlagEnum_CardinalDirectionType(
+ int expected,
+ bool includeDefault,
+ CardinalDirectionType cardinalDirectionType)
+ {
+ // Act
+ var actual = EnumHelper.GetIndividualValuesByCombinedValueFromFlagEnum(
+ cardinalDirectionType,
+ includeDefault);
+
+ // Assert
+ Assert.Equal(expected, actual.Count);
+ }
+
+ [Theory]
+ [InlineData(6, false)]
+ [InlineData(7, true)]
+ public void GetIndividualValuesFromEnum_DayOfWeek(
+ int expected,
+ bool includeDefault)
+ {
+ // Act
+ var actual = EnumHelper.GetIndividualValuesFromEnum(includeDefault);
+
+ // Assert
+ Assert.Equal(expected, actual.Count);
+ }
}
\ No newline at end of file
diff --git a/test/Atc.Tests/Helpers/TaskHelperTests.cs b/test/Atc.Tests/Helpers/TaskHelperTests.cs
index 2a11bebc..ea171a3d 100644
--- a/test/Atc.Tests/Helpers/TaskHelperTests.cs
+++ b/test/Atc.Tests/Helpers/TaskHelperTests.cs
@@ -1,3 +1,5 @@
+// ReSharper disable RedundantAssignment
+// ReSharper disable NotAccessedVariable
namespace Atc.Tests.Helpers;
[SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "OK.")]
@@ -142,6 +144,32 @@ public void RunSyncAndReturnResult()
Assert.Equal(42, actual);
}
+ [Fact]
+ [SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "OK.")]
+ [SuppressMessage("Major Code Smell", "S1854:Unused assignments should be removed", Justification = "OK.")]
+ [SuppressMessage("Blocker Code Smell", "S2699:Tests should include assertions", Justification = "OK.")]
+ public void FireAndForget_Action()
+ {
+ // Act
+ TaskHelper.FireAndForget(() =>
+ {
+ var x = 0;
+ x++;
+ });
+ }
+
+ [Fact]
+ [SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "OK.")]
+ [SuppressMessage("Blocker Code Smell", "S2699:Tests should include assertions", Justification = "OK.")]
+ public void FireAndForget_Task()
+ {
+ // Arrange
+ var doSomethingTask = DoSomethingAndReturnResultAsync();
+
+ // Act
+ TaskHelper.FireAndForget(doSomethingTask);
+ }
+
private static Task DoSomethingAsync()
{
return Task.Delay(100);