mirror of
https://github.com/ICDSystems/ICD.Common.Utils.git
synced 2026-01-11 19:44:55 +00:00
feat[Types]: Add NotifyFlagsChanged, with abstract and generic classes
This commit is contained in:
131
ICD.Common.Utils.Tests/Types/GenericNotifyFlagsChangedTest.cs
Normal file
131
ICD.Common.Utils.Tests/Types/GenericNotifyFlagsChangedTest.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -242,6 +242,8 @@
|
|||||||
<Compile Include="TimeZoneInfo\IcdTimeZoneInfoAdjustmentRule.cs" />
|
<Compile Include="TimeZoneInfo\IcdTimeZoneInfoAdjustmentRule.cs" />
|
||||||
<Compile Include="TimeZoneInfo\IcdTimeZoneInfoTransitionTime.cs" />
|
<Compile Include="TimeZoneInfo\IcdTimeZoneInfoTransitionTime.cs" />
|
||||||
<Compile Include="TryUtils.cs" />
|
<Compile Include="TryUtils.cs" />
|
||||||
|
<Compile Include="Types\AbstractNotifyFlagsChanged.cs" />
|
||||||
|
<Compile Include="Types\GenericNotifyFlagsChanged.cs" />
|
||||||
<Compile Include="UriQueryBuilder.cs" />
|
<Compile Include="UriQueryBuilder.cs" />
|
||||||
<Compile Include="UriUtils.cs" />
|
<Compile Include="UriUtils.cs" />
|
||||||
<Compile Include="VersionSpan.cs" />
|
<Compile Include="VersionSpan.cs" />
|
||||||
|
|||||||
79
ICD.Common.Utils/Types/AbstractNotifyFlagsChanged.cs
Normal file
79
ICD.Common.Utils/Types/AbstractNotifyFlagsChanged.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
34
ICD.Common.Utils/Types/GenericNotifyFlagsChanged.cs
Normal file
34
ICD.Common.Utils/Types/GenericNotifyFlagsChanged.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user