using System;
using System.Collections;
using System.Collections.Generic;
namespace Vanara.Extensions
{
/// Structure to use in place of a enumerated type with the set. Allows for indexer access to flags and simplifies boolean logic.
/// An enumerated type.
///
/// Use this structure by replacing an enumerated type field for simpler access. See old and new way examples below:
/// var fileInfo = new FileInfo(@"C:\MyFile.txt");
/// FileAttributes fileAttr = fileInfo.Attributes;
/// if ((fileAttr & FileAttributes.Hidden) != FileAttributes.Hidden)
/// {
/// Console.WriteLine("The file is hidden. Trying to unhide now.");
/// fileInfo.Attributes = (fileAttr & ~FileAttributes.Hidden);
/// }
/// var fileInfo = new FileInfo(@"C:\MyFile.txt");
/// EnumFlagIndexer<FileAttributes> fileAttr = fileInfo.Attributes;
/// if (fileAttr[FileAttributes.Hidden])
/// {
/// Console.WriteLine("The file is hidden. Trying to unhide now.");
/// fileAttr[FileAttributes.Hidden] = false;
/// fileInfo.Attributes = fileAttr;
/// }
///
public struct EnumFlagIndexer : IEquatable, IEquatable>, IEnumerable where TEnum : System.Enum
{
private TEnum flags;
/// Initializes a new instance of the struct.
/// The initial value. Defaults to default(E).
public EnumFlagIndexer(TEnum initialValue)
{
if (!typeof(TEnum).IsEnum)
throw new ArgumentException($"Type '{typeof(TEnum).FullName}' is not an enum");
if (!Attribute.IsDefined(typeof(TEnum), typeof(FlagsAttribute)))
throw new ArgumentException($"Type '{typeof(TEnum).FullName}' doesn't have the 'Flags' attribute");
flags = initialValue;
}
/// Gets or sets the specified flag.
/// A boolean value representing the presence of the specified enumerated flag.
/// A value in the enumerated type to check.
/// true if the flag is set; false otherwise.
public bool this[TEnum flag]
{
get => (Convert.ToInt64(flags) & Convert.ToInt64(flag)) != 0;
set
{
var flagsValue = Convert.ToInt64(flags);
var flagValue = Convert.ToInt64(flag);
if (value)
flagsValue |= flagValue;
else
flagsValue &= ~flagValue;
flags = (TEnum)Enum.ToObject(typeof(TEnum), flagsValue);
}
}
/// Implements the operator !=.
/// An instance of .
/// An instance of the enumerated type.
/// The result of the operator.
public static bool operator !=(EnumFlagIndexer a, TEnum b) => !a.Equals(b);
/// Implements the operator &.
/// An instance of .
/// An instance of the enumerated type.
/// The result of the operator.
public static TEnum operator &(EnumFlagIndexer a, TEnum b) => (TEnum)Enum.ToObject(typeof(TEnum), Convert.ToInt64(a.flags) & Convert.ToInt64(b));
/// Implements the operator |.
/// An instance of .
/// An instance of the enumerated type.
/// The result of the operator.
public static TEnum operator |(EnumFlagIndexer a, TEnum b) => (TEnum)Enum.ToObject(typeof(TEnum), Convert.ToInt64(a.flags) | Convert.ToInt64(b));
/// Implements the operator ==.
/// An instance of .
/// An instance of the enumerated type.
/// The result of the operator.
public static bool operator ==(EnumFlagIndexer a, TEnum b) => a.Equals(b);
/// Implicitly converts an instance of to the value of enumerated type E.
/// The f.
/// The result of the operator.
public static implicit operator TEnum(EnumFlagIndexer f) => f.flags;
/// Implicitly converts a value of E to an instance of .
/// The e.
/// The result of the operator.
public static implicit operator EnumFlagIndexer(TEnum e) => new EnumFlagIndexer(e);
/// Clears and sets to default(E).
public void Clear()
{
flags = default;
}
/// Indicates whether the current object is equal to another object of the same type.
/// An object to compare with this object.
/// true if the current object is equal to the parameter; otherwise, false.
public bool Equals(TEnum other) => Convert.ToInt64(flags) == Convert.ToInt64(other);
/// Indicates whether the current object is equal to another object of the same type.
/// An object to compare with this object.
/// true if the current object is equal to the parameter; otherwise, false.
public bool Equals(EnumFlagIndexer other) => Convert.ToInt64(flags) == Convert.ToInt64(other.flags);
/// Determines whether the specified , is equal to this instance.
/// The to compare with this instance.
/// true if the specified is equal to this instance; otherwise, false.
public override bool Equals(object obj) => Equals(obj, flags);
/// Returns a hash code for this instance.
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
public override int GetHashCode() => flags.GetHashCode();
/// Returns an enumerator that iterates through a collection.
/// An object that can be used to iterate through the collection.
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// Returns an enumerator that iterates through the collection.
/// A that can be used to iterate through the collection.
public IEnumerator GetEnumerator()
{
long t = 0;
foreach (TEnum e in Enum.GetValues(typeof(TEnum)))
if (this[e]) { t |= Convert.ToInt64(e); yield return e; }
var rem = Convert.ToInt64(flags) ^ t;
if (rem != 0) yield return (TEnum)Enum.ToObject(typeof(TEnum), rem);
}
/// Returns a that represents this instance.
/// A that represents this instance.
public override string ToString() => flags.ToString();
/// Unions the specified flags.
/// The flags.
public void Union(TEnum enumVal)
{
this[enumVal] = true;
}
/// Unions the specified flags.
/// The flags.
public void Union(IEnumerable enumValues)
{
foreach (var e in enumValues) this[e] = true;
}
}
}