I'm trying to make a helper method for listing the names of all bits set in an Enum value (for logging purposes). I want have a method that would return the list of all the Enum values set in some variables. In my example
[Flag]
Enum HWResponse
{
None = 0x0,
Ready = 0x1,
Working = 0x2,
Error = 0x80,
}
I feed it 0x81, and it should provide me with a IEnumerable<HWResponse> containing {Ready, Error}.
As I didn't find a simpler way, I tried to write the code below, but I can't make it compile.
public static IEnumerable<T> MaskToList<T>(Enum mask)
{
if (typeof(T).IsSubclassOf(typeof(Enum)) == false)
throw new ArgumentException();
List<T> toreturn = new List<T>(100);
foreach(T curValueBit in Enum.GetValues(typeof (T)).Cast<T>())
{
Enum bit = ((Enum) curValueBit); // Here is the error
if (mask.HasFlag(bit))
toreturn.Add(curValueBit);
}
return toreturn;
}
On this version of the code, the compiler complains that it can't cast T to Enum.
What did I do wrong? Is there a better (simpler) way to do this? How could I make the cast?
Also, I tried to write the method as
public static IEnumerable<T> MaskToList<T>(Enum mask) where T:Enum
but Enum is of a special type that forbids the 'where' syntax (Using C# 4.0)
Here's a simple way to write it using LINQ:
public static IEnumerable<T> MaskToList<T>(Enum mask)
{
if (typeof(T).IsSubclassOf(typeof(Enum)) == false)
throw new ArgumentException();
return Enum.GetValues(typeof(T))
.Cast<Enum>()
.Where(m => mask.HasFlag(m))
.Cast<T>();
}
If your desired end result is a string list of names, just call mask.ToString().
What would you do if the enum were defined like this:
[Flags]
enum State
{
Ready = 1,
Waiting = 2,
ReadyAndWaiting = 3
}
As to resolving the compiler error, this should do it:
Enum bit = (Enum)(object)curValueBit;
Jon Skeet has a project called unconstrained melody that allows you to add the enum constraint, after compilation, by rewriting the IL. This works because the CLR supports such a constraint, even though C# does not.
Another thought: It will be more efficient to cast the return value of GetValues directly to T[]:
foreach(T curValueBit in (T[])Enum.GetValues(typeof (T)))
Building on Gabe's answer I came up with this :
public static class EnumHelper<T>
where T : struct
{
// ReSharper disable StaticFieldInGenericType
private static readonly Enum[] Values;
// ReSharper restore StaticFieldInGenericType
private static readonly T DefaultValue;
static EnumHelper()
{
var type = typeof(T);
if (type.IsSubclassOf(typeof(Enum)) == false)
{
throw new ArgumentException();
}
Values = Enum.GetValues(type).Cast<Enum>().ToArray();
DefaultValue = default(T);
}
public static T[] MaskToList(Enum mask, bool ignoreDefault = true)
{
var q = Values.Where(mask.HasFlag);
if (ignoreDefault)
{
q = q.Where(v => !v.Equals(DefaultValue));
}
return q.Cast<T>().ToArray();
}
}
I organized things a bit differently, namely I put the type check (i.e.: the verification that T is really an enumeration) and the obtaining of the enum values in the static constructor so this is done only once (this would be a performance improvement).
Another thing, I added an optional parameter so you can ignore the typical "zero" / "None" / "NotApplicable" / "Undefined" / etc value of the enumeration.
I spent some time on searching how to convert Flags enum value to List.
I have found pretty simple solution, maybe it will help someone.
[Flags]
public enum Tag
{
None = 0,
Stablecoin = 1,
NativeTokens = 2,
Dex = 4
}
var values = Tag.Stablecoin | Tag.Dex;
var str = values.ToString(); //"Stablecoin, Dex"
var list = uniqueNftTagsV2.Split(", "); //{"Stablecoin","Dex"}
What if just do something like this:
public static IEnumerable<T> MaskToList<T>(Enum mask)
{
if (typeof(T).IsSubclassOf(typeof(Enum)) == false)
throw new ArgumentException();
List<T> toreturn = new List<T>(100);
foreach(T curValueBit in Enum.GetValues(typeof (T)).Cast<T>())
{
Enum bit = (curValueBit as Enum); // The only difference is actually here,
// use "as", instead of (Enum) cast
if (mask.HasFlag(bit))
toreturn.Add(curValueBit);
}
return toreturn;
}
As the as has not compile time check. Compiler here just "believes" you, hoping that you know what you're doing, so the compile time error not raised.
Related
I have a structure that contains an enum:
public enum MyEnum
{
happy = 0,
sad
}
public struct MyStruct
{
public MyEnum feelings;
public int boopCounter;
}
and then I am given a text/string version of a structure and its contents:
feelings = sad
boopCounter = 12
I am trying to write a generic parser that can generate a struct object with the correctly populated fields, without having to modify the parser every time the structure gets updated. I have been generally successful using Reflections:
// Scan through each member of the structure, looking for a match
foreach (var field in typeof(MyStruct).GetFields(System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Public))
{
if(field.Name == fieldNameFromText)
{
// This is the member we need to update. Treat enums differently
var fld = typeof(MyStruct).GetField(field.Name);
if(field.FieldType.IsEnum)
{
//fld.SetValue(s, Enum.ToObject(field.FieldType, valFromText)); // Didn't work
fld.SetValue(s, Convert.ChangeType(Enum.ToObject(field.FieldType, Convert.ToInt32(valFromText)), field.FieldType)); // Worked, but only if integer version of enum is passed in
}
else
{
// Not an enum, parse directly.
fld.SetValue(s, Convert.ChangeType(valFromText, field.FieldType));
}
break;
}
}
So this code works, but it only works if my input text contains the integer version of the enum:
feelings = 1
boopCounter = 12
Is there a way to get this to work with the original string enumeration input ("sad") ? I'd like to keep it generic if possible (notice how my code doesn't ever specifically call out "MyEnum" anywhere).
Yes, you can use the non-generic version of Enum.Parse.
var fieldType = fld.FieldType;
if (fieldType.IsEnum)
{
var valueToAssign = Enum.Parse(fieldType, valFromText);
fld.SetValue(s, valueToAssign);
}
If I have enum:
public enum ImportState : byte
{
None = 0,
ImportedWithChanges = 44,
AwaitingApproval = 45,
Removing = 66,
Revalidating = 99,
};
How to get enum order?
For example:
GetOrder(ImportState.None)
Should return 1(first in order)
GetOrder(ImportState.AwaitingApproval )
Should return 3 (third in order)
here is the missing method GetOrder
public static int GetOrder(ImportState State)
{
return Enum.GetValues(typeof(ImportState)).Cast<ImportState>().Select((x, i) => new { item = x, index = i }).Single(x => x.item == State).index;
}
As other noticed, Enum.GetValues() returns the values of an enum sorted by value. Perhaps this isn't what you wanted... So, using a little reflection:
public class EnumOrder<TEnum> where TEnum : struct
{
private static readonly TEnum[] Values;
static EnumOrder()
{
var fields = typeof(Values).GetFields(BindingFlags.Static | BindingFlags.Public);
Values = Array.ConvertAll(fields, x => (TEnum)x.GetValue(null));
}
public static int IndexOf(TEnum value)
{
return Array.IndexOf(Values, value);
}
}
Example of use:
public enum Values
{
Foo = 10,
Bar = 1
}
int ix = EnumOrder<Values>.IndexOf(Values.Bar); // 1
Note that the C# specifications aren't clear if the "source code" ordering of an enum is maintained in the compiled program... At this time the C# compiler seems to maintain it, but there is no guarantee in the future...
The only two references I've found are:
Forward declarations are never needed in C# because, with very few exceptions, declaration order is insignificant
and
Declaration order for enum member declarations (§14.3) is significant when constant-expression values are omitted.
So as written, for the example I gave, the ordering is undefined and depends on the C# compiler!
Enumerating the enum values, casting to an IEnumerable, converting to a List. This it is a simple matter of using IndexOf().
Note that for this to work, the enum must be declared in increasing order.
namespace ConsoleApplication1
{
using System;
using System.Linq;
class Program
{
public enum ImportState : byte
{
None = 0,
ImportedWithChanges = 44,
AwaitingApproval = 45,
Removing = 66,
Revalidating = 99,
}
static void Main(string[] args)
{
Console.WriteLine(GetOrder(ImportState.None));
Console.WriteLine(GetOrder(ImportState.AwaitingApproval));
}
public static int GetOrder(ImportState state)
{
var enumValues = Enum.GetValues(typeof(ImportState)).Cast<ImportState>().ToList();
return enumValues.IndexOf(state) + 1; // +1 as the IndexOf() is zero-based
}
}
}
1
3
Press any key to continue . . .
Sth. like this?
int i = 0;
foreach (ImportState state in Enum.GetValues(typeof(ImportState)))
{
i++;
if (state == myState) return i;
}
However there is no real use for this, as enums do not provide an indexed enumeration in themselfes. They represent a value which is more what you´re probably after.
You can use this LINQ query:
int position = Enum.GetValues(typeof(ImportState)).Cast<ImportState>()
//.OrderBy(impState => (int)impState)
.TakeWhile(impState => impState != ImportState.None)
.Count() + 1;
It orders by the int-value of the enum-value, then it takes all until the searched value and counts them. I have omitted the OrderBy since Enum.GetValues automatically returns the order according to their int-value.
MSDN:
The elements of the array are sorted by the binary values of the
enumeration constants
Instead of using the order, you would be better to make better use of the flags. Consider the following
public enum ImportState : byte
{
None = 0,
ImportedWithChanges = 2,
AwaitingApproval = 4,
Removing = 6,
Revalidating = 8,
};
(double)state / Enum.GetValues(typeof(ImportState)).Cast<byte>().Max()
Example
Enums don't really have any sense of ordering, using the above probably still isn't perfect but it doesn't involve a made up order.
What about this Solution?
var result = from r in Enum.GetValues<ImportState>()
let expression =
r == ImportState.Revalidating
? 0
: r == ImportState.AwaitingApproval
? 1
: r == ImportState.Removing
? 2
: r == ImportState.ImportedWithChanges
? 3
: 4
orderby expression ascending
select r.ToString();
Console.WriteLine(string.Join(", ", result));
Output: Revalidating, AwaitingApproval, Removing, ImportedWithChanges, None
I have an enum that looks like this:
enum foo{
a=0,
b=1,
c=2,
d=4
}
Building the flag / bitmask is fine, but is it possible to do something like
int i = 3;
var bar = Enum.Split(foo,i);
Resulting in something like
bar = foo[]{a, b,c};
Thanks.
Try the following
public static IEnumerable<T> Split<T>(int value) {
foreach (object cur in Enum.GetValues(typeof(T))) {
var number = (int)(object)(T)cur;
if (0 != (number & value)) {
yield return (T)cur;
}
}
}
With this you can now write
int i = 3;
IEnumerable<foo> e = Split<foo>(i);
Note: This only works on enum values which derive from int (the default setting). It's also not entirely type safe as T can't be constrained to only enum values (nothing to do about that though)
You can use the FlagsAttribute on the enum and get lots of the functionality for free (no need to work at the bit level).
MSDN describes the attribute as:
Indicates that an enumeration can be treated as a bit field; that is, a set of flags.
The [FlagsAttribute] allows you to extract all of the valid values.
http://msdn.microsoft.com/en-us/library/system.flagsattribute.aspx
Try this:
TEnum[] EnumSplit<TEnum>(int mask)
{
List<TEnum> values = new List<TEnum>();
foreach(int enumValue in Enum.GetValues(typeof(TEnum)))
{
if(mask & enumValue == enumValue)
values.Add((TEnum)enumValue);
}
return values.ToArray();
}
Call it like this:
var bar = EnumSplit<foo>(i);
Preferably, you want to change it to return IEnumerable<TEnum> instead of TEnum[].
You could do this with a method which pulls the values from the Enum you pass:
public static T[] EnumSplit<T>(int value) where T : struct
{
// Simplified as Enum.GetValues will complain if T is not an enum
// However, you should add a check to make sure T implements FlagAttribute
return (from vv in Enum.GetValues(typeof(T))
where ((int)vv & value) != 0
select (T)vv).ToArray();;
}
var flags = Enum.GetValues(typeof (/* YourEnumType */))
.Cast</* YourEnumType */>()
.Select(v => enumValue.HasFlag(v))
.ToArray();
I have a combo box where I am displaying some entries like:
Equals
Not Equals
Less Than
Greater Than
Notice that these strings contain spaces. I have a enum defined which matches to these entries like:
enum Operation{Equals, Not_Equals, Less_Than, Greater_Than};
Since space is not allowed, I have used _ character.
Now, is there any way to convert given string automatically to an enum element without writing a loop or a set of if conditions my self in C#?
I suggest building a Dictionary<string, Operation> to map friendly names to enum constants and use normal naming conventions in the elements themselves.
enum Operation{ Equals, NotEquals, LessThan, GreaterThan };
var dict = new Dictionary<string, Operation> {
{ "Equals", Operation.Equals },
{ "Not Equals", Operation.NotEquals },
{ "Less Than", Operation.LessThan },
{ "Greater Than", Operation.GreaterThan }
};
var op = dict[str];
Alternatively, if you want to stick to your current method, you can do (which I recommend against doing):
var op = (Operation)Enum.Parse(typeof(Operation), str.Replace(' ', '_'));
Operation enumVal = (Operation)Enum.Parse(typeof(Operation), "Equals")
For "Not Equals", you obv need to replace spaces with underscores in the above statement
EDIT: The following version replaces the spaces with underscores before attempting the parsing:
string someInputText;
var operation = (Operation)Enum.Parse(typeof(Operation), someInputText.Replace(" ", "_"));
Either create a dedicated mapper using a dictionary (per Mehrdad's answer) or implement a TypeConverter.
Your custom TypeConverter could either replace " " -> "_" (and vice versa) or it could reflect the enumeration and use an attribute for determining the display text of the item.
enum Operation
{
[DisplayName("Equals")]
Equals,
[DisplayName("Not Equals")]
Not_Equals,
[DisplayName("Less Than")]
Less_Than,
[DisplayName("Greater Than")]
Greater_Than
};
public class OperationTypeConverter : TypeConverter
{
private static Dictionary<string, Operation> operationMap;
static OperationTypeConverter()
{
BindingFlags bindingFlags = BindingFlags.Static | BindingFlags.GetField
| BindingFlags.Public;
operationMap = enumType.GetFields(bindingFlags).ToDictionary(
c => GetDisplayName(c)
);
}
private static string GetDisplayName(FieldInfo field, Type enumType)
{
DisplayNameAttribute attr = (DisplayNameAttribute)Attribute.GetCustomAttribute(typeof(DisplayNameAttribute));
return (attr != null) ? attr.DisplayName : field.Name;
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
string stringValue = value as string;
if (stringValue != null)
{
Operation operation;
if (operationMap.TryGetValue(stringValue, out operation))
{
return operation;
}
else
{
throw new ArgumentException("Cannot convert '" + stringValue + "' to Operation");
}
}
}
}
This implementation could be improved in several ways:
Make it generic
Implement ConvertTo
Support FlagsAttribute
You can use the Parse method:
Operarion operation = (Operation)Enum.Parse(typeof(Operation), "Not_Equals");
Some examples here
Why use another way : convert Enumeration to String?
Just generate the items of your combo box from your Enumeration.
in C#, you can add extension methods to enum types. See
http://msdn.microsoft.com/en-us/library/bb383974.aspx
You could use this approach to add toString(Operation op), fromString(String str) and toLocalizedString(Operation op) methods to your enum types. The method that you use to lookup the particular string depends on your application and should be consistent with what you do in similar cases. Using a dictionary as others have suggested seems like a good first approach as long as you don't need full localization in your app.
I would use a singleton of this enum mapper class that performs much faster than Enum.Parse (which uses reflection and is really slow).
You can then use EnumFromString(typeof(YourEnum), "stringValue") to get your enum.
As of C# 8 you can do that using switches. In your example I believe the code would be like this.
enum Operation{Equals, Not_Equals, Less_Than, Greater_Than};
public static string OperationString(Operation opString) =>
opString switch
{
Operation.Equals => "Equals",
Operation.Not_Equals => "Not Equals",
Operation.Less_Than=> "Less Than",
Operation.Greater_Than=> "Greater Than",
_ => throw new ArgumentException(message: "invalid enum value", paramName: nameof(opString )),
};
See here for the documentation.
I work on C# window ...vs05 ......i want to put space on enum string ....i do it below code have this.....My problem is after select a value from the combo how can i get the index number of this value .....and if i gone to edit mode then i need to show the value what is user already selected .....how to get enum selected value on basis of index number
public enum States
{
California,
[Description("New Mexico")]
NewMexico,
[Description("New York")]
NewYork,
[Description("South Carolina")]
SouthCarolina,
Tennessee,
Washington
}
public static string GetEnumDescription(Enum value)
{
FieldInfo fi = value.GetType().GetField(value.ToString());
DescriptionAttribute[] attributes =
(DescriptionAttribute[])fi.GetCustomAttributes(
typeof(DescriptionAttribute), false);
if (attributes != null && attributes.Length > 0)
return attributes[0].Description;
else
return value.ToString();
}
public static IEnumerable<T> EnumToList<T>()
{
Type enumType = typeof(T);
// Can't use generic type constraints on value types,
// so have to do check like this
if (enumType.BaseType != typeof(Enum))
throw new ArgumentException("T must be of type System.Enum");
Array enumValArray = Enum.GetValues(enumType);
List<T> enumValList = new List<T>(enumValArray.Length);
foreach (int val in enumValArray)
{
enumValList.Add((T)Enum.Parse(enumType, val.ToString()));
}
return enumValList;
}
private void Form1_Load(object sender, EventArgs e)
{
//cboSurveyRemarksType = new ComboBox();
cboSurveyRemarksType.Items.Clear();
foreach (States state in EnumToList<States>())
{
cboSurveyRemarksType.Items.Add(GetEnumDescription(state));
}
}
You could iterate through the enums available (Enum.GetValues(enumType)) and see which one has the description chosen (not the best performance but it is probably not significant for combo-boxes).
You could create your own attribute, and have the index there as well (ugly in terms of coding practice)
If you want to improve performance on option #1 you could pre-cache a Dictionary<String, Integer> object with the descriptions of each enum, and use that instead.
So you have an integer and you want to convert it back to the enum type? Just cast it. So long as you haven't explicitly specified any values in your enum declaration, and so long as the "index" you've got is 0-based you should be able to do:
States state = (States) stateIndex;
(Note that by normal .NET naming conventions this should be called State by the way - plurals are usually reserved for flags.)
This answer is based on the text of your question rather than your code - I can't see anything in your code which really refers to an index.
Simple. You can cast the selected index (which is an integer) to the States enum. I have written a simple test method which demonstrates a few examples of using the enum that should help clear the concept:
private static void TestMethod()
{
foreach (States state in EnumToList<States>())
{
Console.Write(GetEnumDescription(state) + "\t");
Int32 index = ((Int32)state);
Console.Write(index.ToString() + "\t");
States tempState = (States)index;
Console.WriteLine(tempState.ToString());
}
Console.ReadLine();
}
If you don't understand, would be happy to clarify further.