feat[Types]: Add NotifyFlagsChanged, with abstract and generic classes

This commit is contained in:
Drew Tingen
2023-06-14 06:11:37 -04:00
parent 73a716f9b9
commit 569066edc4
5 changed files with 247 additions and 1 deletions

View File

@@ -26,7 +26,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Condition="'$(TargetFramework)' == 'net472'" Include="Newtonsoft.Json" Version="13.0.1" Aliases="RealNewtonsoft"/>
<PackageReference Condition="'$(TargetFramework)' == 'net472'" Include="Newtonsoft.Json" Version="13.0.1" Aliases="RealNewtonsoft" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
<PackageReference Include="NUnit" Version="3.13.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />

View File

@@ -0,0 +1,131 @@
using System;
using System.Collections.Generic;
using ICD.Common.Utils.EventArguments;
using ICD.Common.Utils.Types;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests.Types
{
[TestFixture]
public class GenericNotifyFlagsChangedTest
{
[Flags]
private enum eTestFlagsEnum
{
None = 0,
A = 1,
B = 2,
C = 4,
D = 32,
BandC = B | C
}
[Test]
public void TestSingleFlags()
{
List<GenericEventArgs<eTestFlagsEnum>> addedFlags = new List<GenericEventArgs<eTestFlagsEnum>>();
List<GenericEventArgs<eTestFlagsEnum>> removedFlags = new List<GenericEventArgs<eTestFlagsEnum>>();
GenericNotifyFlagsChanged<eTestFlagsEnum> genericNotify = new GenericNotifyFlagsChanged<eTestFlagsEnum>();
genericNotify.OnFlagsSet += (sender, args) => addedFlags.Add(args);
genericNotify.OnFlagsUnset += (sender, args) => removedFlags.Add(args);
// Initial State
Assert.AreEqual(0, addedFlags.Count);
Assert.AreEqual(0, removedFlags.Count);
// Add No flags
genericNotify.Data = eTestFlagsEnum.None;
Assert.AreEqual(0, addedFlags.Count);
Assert.AreEqual(0, removedFlags.Count);
// Add Flag
genericNotify.Data = eTestFlagsEnum.B;
Assert.AreEqual(1, addedFlags.Count);
Assert.AreEqual(0, removedFlags.Count);
Assert.AreEqual(eTestFlagsEnum.B, addedFlags[0].Data);
Assert.AreEqual(eTestFlagsEnum.B, genericNotify.Data);
// Add Another Flag
genericNotify.Data |= eTestFlagsEnum.C;
Assert.AreEqual(2, addedFlags.Count);
Assert.AreEqual(0, removedFlags.Count);
Assert.AreEqual(eTestFlagsEnum.C, addedFlags[1].Data);
Assert.AreEqual(eTestFlagsEnum.B | eTestFlagsEnum.C, genericNotify.Data);
// Remove a Flag
genericNotify.Data = genericNotify.Data & ~ eTestFlagsEnum.B;
Assert.AreEqual(2, addedFlags.Count);
Assert.AreEqual(1, removedFlags.Count);
Assert.AreEqual(eTestFlagsEnum.B, removedFlags[0].Data);
Assert.AreEqual(eTestFlagsEnum.C, genericNotify.Data);
// Add Already Existing Flags
genericNotify.Data |= eTestFlagsEnum.C;
Assert.AreEqual(2, addedFlags.Count);
Assert.AreEqual(1, removedFlags.Count);
// Clear Flags
genericNotify.Data = eTestFlagsEnum.None;
Assert.AreEqual(2, addedFlags.Count);
Assert.AreEqual(2, removedFlags.Count);
Assert.AreEqual(eTestFlagsEnum.C, removedFlags[1].Data);
Assert.AreEqual(eTestFlagsEnum.None, genericNotify.Data);
}
[Test]
public void TestMultipleFlags()
{
List<GenericEventArgs<eTestFlagsEnum>> addedFlags = new List<GenericEventArgs<eTestFlagsEnum>>();
List<GenericEventArgs<eTestFlagsEnum>> removedFlags = new List<GenericEventArgs<eTestFlagsEnum>>();
GenericNotifyFlagsChanged<eTestFlagsEnum> genericNotify = new GenericNotifyFlagsChanged<eTestFlagsEnum>();
genericNotify.OnFlagsSet += (sender, args) => addedFlags.Add(args);
genericNotify.OnFlagsUnset += (sender, args) => removedFlags.Add(args);
// Initial State
Assert.AreEqual(0, addedFlags.Count);
Assert.AreEqual(0, removedFlags.Count);
// Add No flags
genericNotify.Data = eTestFlagsEnum.None;
Assert.AreEqual(0, addedFlags.Count);
Assert.AreEqual(0, removedFlags.Count);
// Add Flag
genericNotify.Data = eTestFlagsEnum.B | eTestFlagsEnum.D;
Assert.AreEqual(1, addedFlags.Count);
Assert.AreEqual(0, removedFlags.Count);
Assert.AreEqual(eTestFlagsEnum.B | eTestFlagsEnum.D, addedFlags[0].Data);
Assert.AreEqual(eTestFlagsEnum.B | eTestFlagsEnum.D, genericNotify.Data);
// Add Another Flag
genericNotify.Data |= eTestFlagsEnum.C;
Assert.AreEqual(2, addedFlags.Count);
Assert.AreEqual(0, removedFlags.Count);
Assert.AreEqual(eTestFlagsEnum.C, addedFlags[1].Data);
Assert.AreEqual(eTestFlagsEnum.B | eTestFlagsEnum.C | eTestFlagsEnum.D, genericNotify.Data);
// Remove a Flag
genericNotify.Data = eTestFlagsEnum.D;
Assert.AreEqual(2, addedFlags.Count);
Assert.AreEqual(1, removedFlags.Count);
Assert.AreEqual(eTestFlagsEnum.B | eTestFlagsEnum.C, removedFlags[0].Data);
Assert.AreEqual(eTestFlagsEnum.D, genericNotify.Data);
// Add Already Existing Flags
genericNotify.Data |= eTestFlagsEnum.D;
Assert.AreEqual(2, addedFlags.Count);
Assert.AreEqual(1, removedFlags.Count);
// Clear Flags
genericNotify.Data = eTestFlagsEnum.None;
Assert.AreEqual(2, addedFlags.Count);
Assert.AreEqual(2, removedFlags.Count);
Assert.AreEqual(eTestFlagsEnum.D, removedFlags[1].Data);
Assert.AreEqual(eTestFlagsEnum.None, genericNotify.Data);
}
}
}

View File

@@ -242,6 +242,8 @@
<Compile Include="TimeZoneInfo\IcdTimeZoneInfoAdjustmentRule.cs" />
<Compile Include="TimeZoneInfo\IcdTimeZoneInfoTransitionTime.cs" />
<Compile Include="TryUtils.cs" />
<Compile Include="Types\AbstractNotifyFlagsChanged.cs" />
<Compile Include="Types\GenericNotifyFlagsChanged.cs" />
<Compile Include="UriQueryBuilder.cs" />
<Compile Include="UriUtils.cs" />
<Compile Include="VersionSpan.cs" />

View File

@@ -0,0 +1,79 @@
using System;
using ICD.Common.Utils.EventArguments;
namespace ICD.Common.Utils.Types
{
public abstract class AbstractNotifyFlagsChanged<T> where T : struct, IConvertible
{
private T m_Data;
/// <summary>
/// Raised when flags are set on the data
/// </summary>
public event EventHandler<GenericEventArgs<T>> OnFlagsSet;
/// <summary>
/// Raised when flags are unset on the data
/// </summary>
public event EventHandler<GenericEventArgs<T>> OnFlagsUnset;
/// <summary>
/// Raised when the data changes
/// </summary>
public event EventHandler<GenericEventArgs<T>> OnChange;
/// <summary>
/// Data
/// </summary>
public T Data
{
get { return m_Data; }
set
{
if (DataIsEqual(m_Data, value))
return;
int intData = GetIntValue(m_Data);
int intValue = GetIntValue(value);
int setFlags = intValue & ~intData;
int unsetFlags = intData & ~intValue;
m_Data = value;
OnChange.Raise(this, value);
if (setFlags != 0)
OnFlagsSet.Raise(this, GetEnumValue(setFlags));
if (unsetFlags != 0)
OnFlagsUnset.Raise(this, GetEnumValue(unsetFlags));
}
}
/// <summary>
/// Converts the enum to the backing int value
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
protected abstract int GetIntValue(T value);
/// <summary>
/// Converts the backing int value to enum
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
protected abstract T GetEnumValue(int value);
/// <summary>
/// Checks enums for equality
/// Override for performance improvements
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
protected virtual bool DataIsEqual(T a, T b)
{
return GetIntValue(a) == GetIntValue(b);
}
}
}

View File

@@ -0,0 +1,34 @@
using System;
using ICD.Common.Utils.EventArguments;
namespace ICD.Common.Utils.Types
{
/// <summary>
/// Class to raise events when flags are set and unset on the data
/// </summary>
/// <typeparam name="T"></typeparam>
public sealed class GenericNotifyFlagsChanged<T> : AbstractNotifyFlagsChanged<T> where T : struct, IConvertible
{
/// <summary>
/// Converts the enum to the backing int value
/// Override for performance improvements
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
protected override int GetIntValue(T value)
{
return (int)(object)value;
}
/// <summary>
/// Converts the backing int value to enum
/// Override for performance improvements
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
protected override T GetEnumValue(int value)
{
return (T)(object)value;
}
}
}