[英]C#: Enum.IsDefined on combined flags
我有這個枚舉:
[Flags]
public enum ExportFormat
{
None = 0,
Csv = 1,
Tsv = 2,
Excel = 4,
All = Excel | Csv | Tsv
}
我試圖在這個(或任何,真的)枚舉上做一個包裝器,它會通知更改。 目前它看起來像這樣:
public class NotifyingEnum<T> : INotifyPropertyChanged
where T : struct
{
private T value;
public event PropertyChangedEventHandler PropertyChanged;
public NotifyingEnum()
{
if (!typeof (T).IsEnum)
throw new ArgumentException("Type T must be an Enum");
}
public T Value
{
get { return value; }
set
{
if (!Enum.IsDefined(typeof (T), value))
throw new ArgumentOutOfRangeException("value", value, "Value not defined in enum, " + typeof (T).Name);
if (!this.value.Equals(value))
{
this.value = value;
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs("Value"));
}
}
}
}
由於枚舉確實可以分配任何值,因此我想檢查是否定義了給定的值。 但是我發現了一個問題。 如果我在這里給它一個枚舉,例如Csv | Excel
Csv | Excel
,然后Enum.IsDefined
將返回false
。 顯然是因為我沒有定義任何由這兩個組成的枚舉。 我想這在某種程度上是合乎邏輯的,但是我應該如何檢查給定的值是否有效? 換句話說,為了讓它工作,我需要用什么來交換下面的行?
if (!Enum.IsDefined(typeof (T), value))
我們知道轉換為字符串的枚舉值永遠不會以數字開頭,但具有無效值的數字總是會。 這是最簡單的解決方案:
public static bool IsDefinedEx(this Enum yourEnum)
{
char firstDigit = yourEnum.ToString()[0];
if (Char.IsDigit(firstDigit) || firstDigit == '-') // Account for signed enums too..
return false;
return true;
}
使用該擴展方法而不是股票 IsDefined 應該可以解決您的問題。
對於基於標志的枚舉,關鍵在於是否設置了位。 因此,對於“ExportFormat”,如果設置了第 1 位,則它是 CSV 格式,即使可能設置了更多位。 位 1 和位 2 是否設置了無效值? 這是主觀的:從作為一個組的值的角度來看,它是無效的(沒有為第 1 位和第 2 位集定義位模式)但是,因為每個值都是一個位,單獨查看它們,可能是位 1 和 2 設置的值有效。
如果傳入值 0011111011,這是一個有效值嗎? 嗯,這取決於您要查找的內容:如果您查看的是整個值,那么它是一個無效值,但是如果您查看的是單個位,則它是一個不錯的值:它設置了一些位,但不是已定義,但沒關系,因為基於標志的枚舉是“按位”檢查的:您不是將它們與值進行比較,而是檢查是否設置了位。
因此,由於您的邏輯將檢查設置了哪些位以選擇要選擇的格式,因此實際上沒有必要檢查是否定義了枚舉值:您有 3 種格式:如果設置了相應格式的位,則格式為被選中。 這就是你應該寫的邏輯。
我會在位級別進行操作並檢查新值中設置的所有位是否都設置在您的All
值中:
if ( ! (All & NewValue) == NewValue )
您將不得不看看自己如何最好地做到這一點,也許您需要將所有值轉換為 int 然后進行按位比較。
[Flags] enum E { None = 0, A = '1', B = '2', C = '4' }
public static bool IsDefined<T>(T value) where T : Enum
{
var values = Enum.GetValues(typeof(T)).OfType<dynamic>().Aggregate((e1, e2) => (e1 | e2));
return (values & value) == value;
}
// IsDefined(ExportFormat.Csv); // => True
// IsDefined(ExportFormat.All); // => True
// IsDefined(ExportFormat.All | ExportFormat.None); // => True
// IsDefined(ExportFormat.All | ExportFormat.Csv); // => True
// IsDefined((ExportFormat)16); // => False
// IsDefined((ExportFormat)int.MaxValue); // => False
// IsDefined(E.A); // => True
// IsDefined(E.A | E.B); // => True
// IsDefined((E)('1' | '2')); // => True
// IsDefined((E)('5')); // => True
// IsDefined((E)5); // => True
// IsDefined((E)8); // => False
// IsDefined((E)int.MaxValue); // => False
這是一個有效的小擴展方法。
static void Main(string[] args)
{
var x = ExportFormat.Csv | ExportFormat.Excel;
var y = ExportFormat.Csv | ExportFormat.Word;
var z = (ExportFormat)16; //undefined value
var xx = x.IsDefined(); //true
var yy = y.IsDefined(); //false
var zz = z.IsDefined(); //false
}
public static bool IsDefined(this Enum value)
{
if (value == null) return false;
foreach (Enum item in Enum.GetValues(value.GetType()))
if (item.HasFlag(value)) return true;
return false;
}
[Flags]
public enum ExportFormat
{
None = 0,
Csv = 1,
Tsv = 2,
Excel = 4,
Word = 8,
All = Excel | Csv | Tsv
}
以下方法適用於未在枚舉中分組的由代碼組合的項目:
static void Main(string[] args)
{
var x = ExportFormat.Csv | ExportFormat.Excel;
var y = ExportFormat.Csv | ExportFormat.Word;
var z = (ExportFormat)16; //undefined value
var xx = x.IsDefined(); //true
var yy = y.IsDefined(); //true
var zz = z.IsDefined(); //false
}
public static bool IsDefined(this ExportFormat value)
{
var max = Enum.GetValues(typeof(ExportFormat)).Cast<ExportFormat>()
.Aggregate((e1,e2) => e1 | e2);
return (max & value) == value;
}
如果您使用的是支持 DLR 的 C# 4.0,您可以使用以下很酷的不可知擴展方法:
public static bool IsDefined(this Enum value)
{
dynamic dyn = value;
var max = Enum.GetValues(value.GetType()).Cast<dynamic>().
Aggregate((e1,e2) => e1 | e2);
return (max & dyn) == dyn;
}
注意 - 必須這樣做,因為:
|
和&
不能應用於Enum
和Enum
類型的操作數這是一種方法(使用 Linq):
private static bool IsDefined<T>(long value) where T : struct
{
var max = Enum.GetValues(typeof(T)).Cast<T>()
.Select(v => Convert.ToInt64(v)).
Aggregate((e1, e2) => e1 | e2);
return (max & value) == value;
}
也許嘗試用解析捕捉?
您不想傳遞哪些值?
public T Value
{
get { return value; }
set
{
try
{
Enum.Parse(typeof(T), value.ToString());
}
catch
{
throw new ArgumentOutOfRangeException("value", value, "Value not defined in enum, " + typeof(T).Name);
}
if (!this.value.Equals(value))
{
this.value = value;
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs("Value"));
}
}
}
枚舉值的任何有效組合都會產生一個非數字值:
public static class EnumExtensions
{
public static bool IsDefined(this Enum value) => !ulong.TryParse(value.ToString(), out _);
}
我知道這個帖子已經有一段時間沒有人回答了,但我認為使用內置函數回答它對那些在我之后訪問這個帖子的人有好處。
使用 OP 的原始枚舉,您可以使用以下代碼解析位掩碼值。
ExportFormat format;
if (!Enum.TryParse<ExportFormat>(value.ToString(), out format))
{
// Could not parse
}
希望有幫助。
見這里。 相當多的代碼。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.