試煉17 - Enum + Flags 無限可能

2022 鐵人賽文 搬回點部落

開始試煉

Flag Enum 如果還不知道是啥的話 請先看下面的文章
目前我看過最棒的了 說明得非常清楚
C#: 位元旗標 (Bit flag) 與列舉

其實在用Flag Enum時都會搭配tools
提供這組擴充方法 給大家參考
Enum Flag Extensions

用權限當範例
一個Role有哪些權限 搭配Flag Enum是很適合的 這樣記錄到DB只需要開一個欄位
用Enum Flag Extensions當基底寫一個範例code
只要有好工具 Flag Enum 超好用的

void Main()
{
	// start with a value
	PermissionTypes permissions = PermissionTypes.Read | PermissionTypes.Write;

	//then check for the values
	bool canRead = permissions.Has(PermissionTypes.Read).Dump("canRead");
	bool canWrite = permissions.Has(PermissionTypes.Write).Dump("canWrite");
	bool canDelete = permissions.Has(PermissionTypes.Delete).Dump("canDelete");
	permissions.Has(PermissionTypes.Delete).Dump("permissions.Has(PermissionTypes.Delete)");
	var p2 = permissions.Include(PermissionTypes.Delete);
	p2.Has(PermissionTypes.Delete).Dump("p2.Has(PermissionTypes.Delete)");
	var p3 = permissions.Remove(PermissionTypes.Delete);
	p3.Has(PermissionTypes.Delete).Dump("p3.Has(PermissionTypes.Delete)");
	p3.Missing(PermissionTypes.Delete).Dump("p3.Missing(PermissionTypes.Delete)");
}

[Flags]
internal enum PermissionTypes
{
	None = 0,
	Read = 1,
	Write = 2,
	Modify = 4,
	Delete = 8,
	Create = 16,
	All = Read | Write | Modify | Delete | Create
}
public static class EnumExtensions
{
	/// <summary>
	/// Includes an enumerated type and returns the new value
	/// </summary>
	public static T Include<T>(this Enum value, T append)
	{
		Type type = value.GetType();

		// determine the values
		object result = value;
		var parsed = new Value(append, type);
		if (parsed.Signed != null)
		{
			result = Convert.ToInt64(value) | (long)parsed.Signed;
		}

		// return the final value
		return (T)Enum.Parse(type, result.ToString());
	}

	/// <summary>
	/// Removes an enumerated type and returns the new value
	/// </summary>
	public static T Remove<T>(this Enum value, T remove)
	{
		Type type = value.GetType();

		// determine the values
		object result = value;
		var parsed = new Value(remove, type);
		if (parsed.Signed != null)
		{
			result = Convert.ToInt64(value) & ~(long)parsed.Signed;
		}

		// return the final value
		return (T)Enum.Parse(type, result.ToString());
	}

	/// <summary>
	/// Checks if an enumerated type contains a value
	/// </summary>
	public static bool Has<T>(this Enum value, T check)
	{
		Type type = value.GetType();

		//determine the values
		var parsed = new Value(check, type);
		return parsed.Signed != null &&
			(Convert.ToInt64(value) & (long)parsed.Signed) == (long)parsed.Signed;
	}

	/// <summary>
	/// Checks if an enumerated type is missing a value
	/// </summary>
	public static bool Missing<T>(this Enum obj, T value)
	{
		return !Has(obj, value);
	}

	/// <summary>
	/// Internal class to simplfy narrowing values between a 
	/// ulong and long since either value should cover any 
	/// lesser value.
	/// </summary>
	private class Value
	{
		public readonly long? Signed;

		// cached comparisons for tye to use
		private static readonly Type _uInt64 = typeof(ulong);
		private static readonly Type _uInt32 = typeof(long);

		public Value(object value, Type type)
		{
			//make sure it is even an enum to work with
			if (!type.IsEnum)
			{
				throw new
				ArgumentException("Value provided is not an enumerated type!");
			}

			//then check for the enumerated value
			Type compare = Enum.GetUnderlyingType(type);

			//if this is an unsigned long then the only
			//value that can hold it would be a ulong
			if (compare == _uInt32 || compare == _uInt64)
			{
				Unsigned = Convert.ToUInt64(value);
			}
			//otherwise, a long should cover anything else
			else
			{
				Signed = Convert.ToInt64(value);
			}
		}

		public ulong? Unsigned { get; set; }
	}
}

結束試煉

Flag Enum 熟練後 可是神兵利器阿

參考
小章哥 滿滿的範例
C#.NET 二進制運算 Enum of Flags

如果內容有誤請多鞭策謝謝