अगर मेरे पास एक फ़्लैग एनम पकड़े हुए वैरिएबल है, तो क्या मैं किसी तरह उस विशिष्ट वैरिएबल में बिट वैल्यू पर पुनरावृति कर सकता हूं? या क्या मुझे Enum.GetValues का उपयोग पूरे एनम पर पुनरावृति करने के लिए करना है और यह देखना है कि कौन से सेट हैं?
अगर मेरे पास एक फ़्लैग एनम पकड़े हुए वैरिएबल है, तो क्या मैं किसी तरह उस विशिष्ट वैरिएबल में बिट वैल्यू पर पुनरावृति कर सकता हूं? या क्या मुझे Enum.GetValues का उपयोग पूरे एनम पर पुनरावृति करने के लिए करना है और यह देखना है कि कौन से सेट हैं?
जवाबों:
static IEnumerable<Enum> GetFlags(Enum input)
{
foreach (Enum value in Enum.GetValues(input.GetType()))
if (input.HasFlag(value))
yield return value;
}
HasFlag
.NET 4 से उपलब्ध है।
Enum.GetValues(input.GetType()).Cast<Enum>().Where(input.HasFlag);
myEnum.GetFLags()
static IEnumerable<Enum> GetFlags(this Enum input)
यहाँ समस्या का एक Linq समाधान है।
public static IEnumerable<Enum> GetFlags(this Enum e)
{
return Enum.GetValues(e.GetType()).Cast<Enum>().Where(e.HasFlag);
}
.Where(v => !Equals((int)(object)v, 0) && e.HasFlag(v));
यदि आपके पास प्रतिनिधित्व करने के लिए शून्य मान है तो उपयोग करेंNone
जहाँ तक मुझे पता है प्रत्येक घटक को प्राप्त करने के लिए कोई बिल्टइन विधियां नहीं हैं। लेकिन यहाँ एक तरह से आप उन्हें प्राप्त कर सकते हैं:
[Flags]
enum Items
{
None = 0x0,
Foo = 0x1,
Bar = 0x2,
Baz = 0x4,
Boo = 0x6,
}
var value = Items.Foo | Items.Bar;
var values = value.ToString()
.Split(new[] { ", " }, StringSplitOptions.None)
.Select(v => (Items)Enum.Parse(typeof(Items), v));
// This method will always end up with the most applicable values
value = Items.Bar | Items.Baz;
values = value.ToString()
.Split(new[] { ", " }, StringSplitOptions.None)
.Select(v => (Items)Enum.Parse(typeof(Items), v)); // Boo
मैंने अनुकूलित किया कि Enum
झंडे को वापस करने के लिए स्ट्रिंग उत्पन्न करने के लिए आंतरिक रूप से क्या करता है। आप परावर्तक में कोड को देख सकते हैं और कम या ज्यादा समतुल्य होना चाहिए। सामान्य उपयोग के मामलों के लिए अच्छी तरह से काम करता है जहां ऐसे मान होते हैं जिनमें कई बिट्स होते हैं।
static class EnumExtensions
{
public static IEnumerable<Enum> GetFlags(this Enum value)
{
return GetFlags(value, Enum.GetValues(value.GetType()).Cast<Enum>().ToArray());
}
public static IEnumerable<Enum> GetIndividualFlags(this Enum value)
{
return GetFlags(value, GetFlagValues(value.GetType()).ToArray());
}
private static IEnumerable<Enum> GetFlags(Enum value, Enum[] values)
{
ulong bits = Convert.ToUInt64(value);
List<Enum> results = new List<Enum>();
for (int i = values.Length - 1; i >= 0; i--)
{
ulong mask = Convert.ToUInt64(values[i]);
if (i == 0 && mask == 0L)
break;
if ((bits & mask) == mask)
{
results.Add(values[i]);
bits -= mask;
}
}
if (bits != 0L)
return Enumerable.Empty<Enum>();
if (Convert.ToUInt64(value) != 0L)
return results.Reverse<Enum>();
if (bits == Convert.ToUInt64(value) && values.Length > 0 && Convert.ToUInt64(values[0]) == 0L)
return values.Take(1);
return Enumerable.Empty<Enum>();
}
private static IEnumerable<Enum> GetFlagValues(Type enumType)
{
ulong flag = 0x1;
foreach (var value in Enum.GetValues(enumType).Cast<Enum>())
{
ulong bits = Convert.ToUInt64(value);
if (bits == 0L)
//yield return value;
continue; // skip the zero value
while (flag < bits) flag <<= 1;
if (flag == bits)
yield return value;
}
}
}
विस्तार विधि में GetIndividualFlags()
एक प्रकार के सभी व्यक्तिगत झंडे मिलते हैं। इसलिए कई बिट्स वाले मानों को छोड़ दिया जाता है।
var value = Items.Bar | Items.Baz;
value.GetFlags(); // Boo
value.GetIndividualFlags(); // Bar, Baz
Bar
, Baz
और Boo
बजाय सिर्फ की Boo
।
Boo
(मान का उपयोग करके लौटा ToString()
)। मैंने इसे केवल व्यक्तिगत झंडे की अनुमति देने के लिए ट्विक किया है। तो मेरे उदाहरण में, आप प्राप्त कर सकते हैं Bar
और Baz
इसके बजाय Boo
।
कुछ साल बाद इस पर वापस आते हुए, थोड़ा और अधिक अनुभव के साथ, एकल-बिट मूल्यों के लिए मेरा अंतिम उत्तर, सबसे कम बिट से उच्चतम बिट तक जा रहा है, जेफ मर्कडो की आंतरिक दिनचर्या का एक मामूली रूप है:
public static IEnumerable<Enum> GetUniqueFlags(this Enum flags)
{
ulong flag = 1;
foreach (var value in Enum.GetValues(flags.GetType()).Cast<Enum>())
{
ulong bits = Convert.ToUInt64(value);
while (flag < bits)
{
flag <<= 1;
}
if (flag == bits && flags.HasFlag(value))
{
yield return value;
}
}
}
यह काम करने लगता है, और कुछ साल पहले मेरी आपत्तियों के बावजूद, मैं यहाँ हैसफलग का उपयोग करता हूं, क्योंकि यह बिटवाइज तुलनाओं की तुलना में कहीं अधिक सुपाठ्य है और गति अंतर मैं किसी भी चीज के लिए महत्वहीन हूं। (यह पूरी तरह से संभव है कि उन्होंने तब से HasFlags की गति में सुधार किया है, सभी के लिए मुझे पता है ... मैंने परीक्षण नहीं किया है।)
yield return bits;
?
@ ग्रेग की विधि से दूर जा रहा है, लेकिन सी # 7.3 से एक नई सुविधा जोड़ रहा है, Enum
बाधा:
public static IEnumerable<T> GetUniqueFlags<T>(this Enum flags)
where T : Enum // New constraint for C# 7.3
{
foreach (Enum value in Enum.GetValues(flags.GetType()))
if (flags.HasFlag(value))
yield return (T)value;
}
नई बाधा के माध्यम से कास्ट करने के लिए बिना, इस एक विस्तार विधि होने की अनुमति देता (int)(object)e
है, और मैं उपयोग कर सकते हैं HasFlag
करने के लिए सीधे विधि और कलाकारों T
से value
।
सी # 7.3 भी delagates के लिए और बाधाओं को जोड़ा unmanaged
।
flags
पैरामीटर को सामान्य प्रकार का होना चाहते थे T
, अन्यथा हर बार जब आप इसे कहते हैं, तो आपको स्पष्ट रूप से एनम प्रकार को निर्दिष्ट करना होगा।
@ RobinHood70 द्वारा दिए गए उत्तर के लिए +1। मैंने पाया कि विधि का एक सामान्य संस्करण मेरे लिए सुविधाजनक था।
public static IEnumerable<T> GetUniqueFlags<T>(this Enum flags)
{
if (!typeof(T).IsEnum)
throw new ArgumentException("The generic type parameter must be an Enum.");
if (flags.GetType() != typeof(T))
throw new ArgumentException("The generic type parameter does not match the target type.");
ulong flag = 1;
foreach (var value in Enum.GetValues(flags.GetType()).Cast<T>())
{
ulong bits = Convert.ToUInt64(value);
while (flag < bits)
{
flag <<= 1;
}
if (flag == bits && flags.HasFlag(value as Enum))
{
yield return value;
}
}
}
EDIT और +1 समाधान स्थान में C # 7.3 लाने के लिए @AustinWBryan के लिए।
public static IEnumerable<T> GetUniqueFlags<T>(this T flags) where T : Enum
{
ulong flag = 1;
foreach (var value in Enum.GetValues(flags.GetType()).Cast<T>())
{
ulong bits = Convert.ToUInt64(value);
while (flag < bits)
{
flag <<= 1;
}
if (flag == bits && flags.HasFlag(value as Enum))
{
yield return value;
}
}
}
आपको सभी मूल्यों को पुनरावृत्त करने की आवश्यकता नहीं है। बस अपने विशिष्ट झंडे की जाँच करें जैसे:
if((myVar & FlagsEnum.Flag1) == FlagsEnum.Flag1)
{
//do something...
}
या (जैसा कि pstrjds ने टिप्पणियों में कहा था) आप इसका उपयोग करने के लिए जाँच कर सकते हैं:
if(myVar.HasFlag(FlagsEnum.Flag1))
{
//do something...
}
मैंने जो किया वह मेरे दृष्टिकोण को बदलने का था, टाइप के तरीके के इनपुट पैरामीटर को टाइप करने के बजाय enum
, मैंने इसे टाइप की एक सरणी के रूप में enum
टाइप किया ( MyEnum[] myEnums
), इस तरह से मैं लूप के अंदर एक स्विच स्टेटमेंट के साथ एरे के माध्यम से पुनरावृति करता हूं।
ऊपर दिए गए जवाबों से संतुष्ट नहीं थे, हालांकि वे शुरुआत थे।
यहाँ कुछ अलग स्रोतों को एक साथ रखने के बाद:
इस धागे के SO QnA
कोड प्रोजेक्ट Enum फ्लैग में पिछला पोस्टर चेक पोस्ट
महान Enum <T> उपयोगिता
मैंने इसे बनाया है इसलिए मुझे बताएं कि आप क्या सोचते हैं।
पैरामीटर::
bool checkZero
इसे 0
ध्वज मूल्य के रूप में अनुमति देने के लिए कहता है । डिफ़ॉल्ट रूप से input = 0
खाली है।
bool checkFlags
: यह जाँचने के लिए बताता है कि क्या Enum
सजाया w / [Flags]
विशेषता है।
पुनश्च। मेरे पास अभी समय नहीं है कि हम इस checkCombinators = false
alg का पता लगा सकें जो इसे किसी भी एनुम मान को अनदेखा करने के लिए मजबूर करेगा जो बिट्स के संयोजन हैं।
public static IEnumerable<TEnum> GetFlags<TEnum>(this TEnum input, bool checkZero = false, bool checkFlags = true, bool checkCombinators = true)
{
Type enumType = typeof(TEnum);
if (!enumType.IsEnum)
yield break;
ulong setBits = Convert.ToUInt64(input);
// if no flags are set, return empty
if (!checkZero && (0 == setBits))
yield break;
// if it's not a flag enum, return empty
if (checkFlags && !input.GetType().IsDefined(typeof(FlagsAttribute), false))
yield break;
if (checkCombinators)
{
// check each enum value mask if it is in input bits
foreach (TEnum value in Enum<TEnum>.GetValues())
{
ulong valMask = Convert.ToUInt64(value);
if ((setBits & valMask) == valMask)
yield return value;
}
}
else
{
// check each enum value mask if it is in input bits
foreach (TEnum value in Enum <TEnum>.GetValues())
{
ulong valMask = Convert.ToUInt64(value);
if ((setBits & valMask) == valMask)
yield return value;
}
}
}
यह हेल्पर क्लास Enum <T> का उपयोग यहां करता है जिसे मैंने यहां उपयोग के yield return
लिए अद्यतन किया है GetValues
:
public static class Enum<TEnum>
{
public static TEnum Parse(string value)
{
return (TEnum)Enum.Parse(typeof(TEnum), value);
}
public static IEnumerable<TEnum> GetValues()
{
foreach (object value in Enum.GetValues(typeof(TEnum)))
yield return ((TEnum)value);
}
}
अंत में, यहाँ इसका उपयोग करने का एक उदाहरण है:
private List<CountType> GetCountTypes(CountType countTypes)
{
List<CountType> cts = new List<CountType>();
foreach (var ct in countTypes.GetFlags())
cts.Add(ct);
return cts;
}
ऊपर दिए गए ग्रेग के उत्तर के आधार पर, यह उस मामले का भी ध्यान रखता है, जहाँ आपके पास आपकी एनुम में मान 0 है, जैसे कोई नहीं = 0. किस स्थिति में, उस मान पर पुनरावृति नहीं होनी चाहिए।
public static IEnumerable<Enum> ToEnumerable(this Enum input)
{
foreach (Enum value in Enum.GetValues(input.GetType()))
if (input.HasFlag(value) && Convert.ToInt64(value) != 0)
yield return value;
}
क्या किसी को पता होगा कि इस पर आगे भी कैसे सुधार किया जाए, ताकि यह उस मामले को संभाल सके जहां Enum में सभी झंडे एक सुपर स्मार्ट तरीके से सेट किए गए हैं जो सभी अंतर्निहित Enum प्रकार और All = ~ 0 और All = EnumValue1 के मामले को संभाल सकते हैं। EnumValue2 | EnumValue3 | ...
आप Enum से एक Iterator का उपयोग कर सकते हैं। MSDN कोड से शुरू:
public class DaysOfTheWeek : System.Collections.IEnumerable
{
int[] dayflag = { 1, 2, 4, 8, 16, 32, 64 };
string[] days = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
public string value { get; set; }
public System.Collections.IEnumerator GetEnumerator()
{
for (int i = 0; i < days.Length; i++)
{
if value >> i & 1 == dayflag[i] {
yield return days[i];
}
}
}
}
यह परीक्षण नहीं किया गया है, इसलिए यदि मुझे कोई गलती हुई तो मुझे बाहर बुलाने के लिए स्वतंत्र महसूस करें। (स्पष्ट रूप से यह फिर से प्रवेश नहीं है।) आपको पहले से मान असाइन करना होगा, या इसे किसी अन्य फ़ंक्शन में तोड़ना होगा जो enum.dayflag और enum.days का उपयोग करता है। आप रूपरेखा के साथ कहीं जाने में सक्षम हो सकते हैं।
यह निम्नलिखित कोड के रूप में अस्वस्थ हो सकता है:
public static string GetEnumString(MyEnum inEnumValue)
{
StringBuilder sb = new StringBuilder();
foreach (MyEnum e in Enum.GetValues(typeof(MyEnum )))
{
if ((e & inEnumValue) != 0)
{
sb.Append(e.ToString());
sb.Append(", ");
}
}
return sb.ToString().Trim().TrimEnd(',');
}
यह तभी अंदर जाता है जब एनुम वैल्यू वैल्यू पर निहित हो
सभी उत्तर सरल झंडे के साथ अच्छी तरह से काम करते हैं, आप संभवतः झंडे के संयुक्त होने पर मुद्दों में शामिल होने जा रहे हैं।
[Flags]
enum Food
{
None=0
Bread=1,
Pasta=2,
Apples=4,
Banana=8,
WithGluten=Bread|Pasta,
Fruits = Apples | Banana,
}
शायद यह जांचने के लिए एक जांच को जोड़ने की जरूरत है कि अगर एनम का मूल्य स्वयं का संयोजन है। आपको अपनी आवश्यकता को पूरा करने के लिए हेनक वैन बोइजेन द्वारा यहां पोस्ट की गई चीज़ की आवश्यकता होगी (आपको थोड़ा नीचे स्क्रॉल करने की आवश्यकता है)
कास्टिंग को रोकने के लिए नई Enum बाधा और जेनरिक का उपयोग कर विस्तार विधि:
public static class EnumExtensions
{
public static T[] GetFlags<T>(this T flagsEnumValue) where T : Enum
{
return Enum
.GetValues(typeof(T))
.Cast<T>()
.Where(e => flagsEnumValue.HasFlag(e))
.ToArray();
}
}
आप इसे सीधे int में कनवर्ट करके कर सकते हैं लेकिन आप टाइपिंग की जाँच ढीली कर देंगे। मुझे लगता है कि सबसे अच्छा तरीका मेरे प्रस्ताव के समान कुछ का उपयोग करना है। यह सभी प्रकार से उचित प्रकार रखता है। कोई रूपांतरण आवश्यक नहीं है। यह बॉक्सिंग के कारण बिल्कुल सही नहीं है जो प्रदर्शन में थोड़ा हिट जोड़ देगा।
बिल्कुल सही (मुक्केबाजी) नहीं, लेकिन यह बिना किसी चेतावनी के काम करता है ...
/// <summary>
/// Return an enumerators of input flag(s)
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static IEnumerable<T> GetFlags<T>(this T input)
{
foreach (Enum value in Enum.GetValues(input.GetType()))
{
if ((int) (object) value != 0) // Just in case somebody has defined an enum with 0.
{
if (((Enum) (object) input).HasFlag(value))
yield return (T) (object) value;
}
}
}
उपयोग:
FileAttributes att = FileAttributes.Normal | FileAttributes.Compressed;
foreach (FileAttributes fa in att.GetFlags())
{
...
}