using System; using System.Collections; using System.Collections.Generic; using System.Linq; 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 : struct, 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 { long flagsValue = Convert.ToInt64(flags); long 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) => obj is TEnum e ? Equals(e) : (obj is EnumFlagIndexer i ? Equals(i) : 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; } } long rem = Convert.ToInt64(flags) ^ t; if (rem != 0) { yield return (TEnum)Enum.ToObject(typeof(TEnum), rem); } } /// Sets the flags to the intersection of the current flags and the specified flags. /// The flags with which to intersect. public void Intersect(IEnumerable enumValues) => flags = Enumerable.Intersect(this, enumValues).CombineFlags(); /// 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 (TEnum e in enumValues) { this[e] = true; } } } }