Generic Enum Flag Parser - c#

I have an array of string values that I would like to have set flags on a Flags Enum object. I have several of these and was looking for a more generic way to pass in the type of the Flags enum and not have to duplicate the method for each Flags Enum type. This is what I have currently:
public static MyFlagsEnum ParseFlagsEnum(string[] values)
{
MyFlagsEnum flags = new MyFlagsEnum();
foreach (var flag in values)
{
flags |= (MyFlagsEnum)Enum.Parse(typeof(MyFlagsEnum), flag);
}
return flags;
}
I was looking for a more generic way of doing the same thing, while being able to use any Flags Enum type.

Enum.Parse can already combine multiple flags. Quoting from the documentation:
Remarks
The value parameter contains the string representation of an enumeration member's underlying value or named constant, or a list of named constants delimited by commas (,). One or more blank spaces can precede or follow each value, name, or comma in value. If value is a list, the return value is the value of the specified names combined with a bitwise OR operation.
So you could do it like this:
public static T ParseFlagsEnum<T>(string[] values)
{
return (T)Enum.Parse(typeof(T), string.Join(",", values));
}

Related

Enum.TryParse strange behaviour

Why does this test pass? TestEnum doesn't contain and option with value "5". So this test should fail, but it doesn't.
private enum TestEnum
{
FirstOption = 2,
SecontOption = 3
}
[Test]
public void EnumTryParseIntValue()
{
TestEnum enumValue;
bool result = Enum.TryParse<TestEnum>(5.ToString(), out enumValue);
Assert.IsTrue(result);
}
Enum.TryParse Method (String, TEnum)
If value is a name that does not correspond to a named constant of
TEnum, the method returns false. If value is the string representation
of an integer that does not represent an underlying value of the TEnum
enumeration, the method returns an enumeration member whose underlying
value is value converted to an integral type. If this behavior is
undesirable, call the IsDefined method to ensure that a particular
string representation of an integer is actually a member of TEnum.
"Returns an enumeration member whose underlying value is value converted to an integral type"
If the value is not present you get back the integer. I don't consider getting back 5 to be an "enumeration member" but that is how it works. If you parse 2 you get FirstOption.
if (Enum.IsDefined(typeof(TestEnum), 5.ToString()))
{
result = Enum.TryParse<TestEnum>(5.ToString(), out enumValue);
Debug.WriteLine(result);
if (result)
{
Debug.WriteLine(enumValue.ToString());
}
}
Use Enum.IsDefined(Type enumType,Object value) - Returns an indication whether a constant with a specified value exists in a specified enumeration.
MSDN: Enum.IsDefined Method

Changing a method from converting just strings to strings and int

Bear with me as this is a difficult concept for me. I have a method that returns the items in an enum as strings (for DB storage). Here is the method:
public static IEnumerable<SelectListItem> GetItemsFromEnum<T>
(T selectedValue = default(T)) where T : struct
{
return from name in Enum.GetNames(typeof(T))
let enumValue = Convert.ToString((T)Enum.
Parse(typeof(T), name, true))
select new SelectListItem
{
Text = GetEnumDescription(name, typeof(T)),
Value = enumValue,
Selected = enumValue.Equals(selectedValue)
};
}
(NB: GetEnumDescription is another method in the class that gets the [Display(Name="") text to display something friendly in the UI.)
For a single select input (radio button, drop down), this works great. However, with multi-selects (list box, check box list), I am thinking of using the enum [Flags] attribute to store in the DB as an int.
However, the GetItemsFromEnum method will not work in the case that I use [Flags] and requires that I change it so that the value assigned in the GetItemsFromEnum method isn't the string value of the enum, it's the int value.
This is actually a two-parter:
How can I modify the method so that enumValue would be the
value of the int from enum [Flags] (i.e., if "1 + 2" were selected
(two check boxes), then the int value saved would be "3")?
Most importantly, what kind of logic can I insert in the method
so that all my other enum's (those without [Flags]) are not
affected (i.e., it still converts ToString)? I was thinking some if ... else logic, but would that work?
First of all, the [Flags] attribute doesn't add special properties to the enum's values, it's just used through Reflection by the Enum class when formatting it as a string and to let people who use your code know that they can use bitwise operations on the enum safely, or in other words, without undesired results.
Let's take this enum:
[Flags]
enum MyEnum
{
Undefined,
Employee,
Student
}
And let's create a variable this way:
var employeeAndStudent = MyEnum.Employee | MyEnum.Student;
The variable, if cast to integer, is equals to 3. While the ToString() method will print "Employee, Student", Enum.GetNames doesn't have a value assigned for the field 3 -- Quite obviously, since it has no name. I can think of two solutions:
You create your own GetNames which will return, in addition to the defined names, also all the possible combinations of the enum's values, conveniently printed in your format.
You define the flags in the enum, which can become pretty wasteful if there are many members. In our case, MyEnum would become:
[Flags]
enum MyEnum
{
Undefined,
Employee,
Student,
EmployeeAndStudent
}
One more thing, your method seems to have a bug. This line:
Selected = enumValue.Equals(selectedValue)
Is trying to compare an enum type to a string, and will always result in false. Are you sure that SelectListItem.Value should be of type string? I suggest you to review your code.
To give direct answers to your question:
Add [Flags] to your enum and use the OR operator to combine values.
In your GetEnumDescription method, check if the type has the Flags attribute.

Ordering of elements in C# enumeration with the same integer value

I have a simple enum:
enum E
{
FullNameForA = 1,
A = 1,
FullNameForB = 2,
B = 2
}
The goal is to be able to use different string values for the same integral values with a twist - FullNameFor* must be used as default. In other words, a user can provide E.A as an input but the code should use E.FullNameForA for output.
It seems like by default C# will use alphabetical ordering of elements with the same integral value, which makes my goal harder. Is that right? Any ideas how to overcome this?
It seems like by default C# will use alphabetical ordering of elements with the same integral value
In what context? When you convert the value back to a string? From the docs of Enum.ToString:
If multiple enumeration members have the same underlying value and you attempt to retrieve the string representation of an enumeration member's name based on its underlying value, your code should not make any assumptions about which name the method will return.
(Note that the decision is in the BCL - it's not a language decision.)
I suggest that if you want a canonical string representation for each value, you create a Dictionary<E, string> and consult that rather than calling ToString().
Consider this alternative solution. You can decorate enum values with the DescriptionAttribute and to have a more human friendly name:
enum E
{
[System.ComponentModel.Description("FullNameForA")]
A = 1
}
Then you can extract the value of that attribute like so:
public static string AsString(this Enum value)
{
var type = value.GetType();
if (!type.IsEnum)
throw new ArgumentException();
var fieldInfo = type.GetField(value.ToString());
if (fieldInfo == null)
return value.ToString();
var attribs = fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false) as DescriptionAttribute[];
return attribs.Length > 0 ? attribs[0].Description : value.ToString();
}
This of course isn't the best performing solution because it relies on reflection.

Enum helper in c# not giving expected result

Basically I am not recieving the correct enum type for some reason and I cannot figure out why, my code is below, many thanks in advance for any pointers/ explanation...
EDIT: type-> changed to anothername (thanks guys for the heads up)
Helper:
public static T Convert<T>(this string str)
{
return (T)Enum.Parse(typeof(T), str, true);
}
Enum values:
public enum anothername
{
SmallText = 100,
Number = 15,
TextArea = 0,
Bool = 0,
Choices = 0,
}
My test:
[Test]
public void EnumGetStringFromEnumType()
{
//arrange
var MaxLength = EnumHelper.Convert<anothername>("TextArea").ToString();
//act
//assert
Assert.AreEqual("TextArea", MaxLength);
}
EDIT:
Thanks, removing the int values solved it!
However... what if I actually wanted to have say values for some enum types and not other e.g.
public enum anothername
{
SmallText = 100,
Number = 15,
TextArea,
Bool,
Choices,
}
Test 2:
[Test]
public void EnumGetIntValueOrStringFromEnumType()
{
//arrange
var MaxLength = EnumHelper.ToEnumSafe<anothername>("TextArea");
//act
//assert
Assert.AreEqual(null, (int)MaxLength);
}
I have exactly the same problem when I try and retrieve the int values, I get incorrect results...
result = 16
The enumeration has duplicate members with the same underlying value as TextArea (Bool and Choices). Although the parse should succeed, the value of ToString on the resulting enum instance is not defined, and may not equal "TextArea" as your assertion is expecting.
From the
Enum.ToString documentation:
If multiple enumeration members have
the same underlying value and you
attempt to retrieve the string
representation of an enumeration
member's name based on its underlying
value, your code should not make any
assumptions about which name the
method will return.
EDIT:
In response to your edit, try this assertion:
var MaxLength = EnumHelper.ToEnumSafe<anothername>("TextArea");
Assert.AreEqual(anotherName.TextArea, MaxLength);
or if you prefer comparing the underlying type:
Assert.AreEqual((int)anotherName.TextArea, (int)MaxLength);
You appear to be under the impression that an enum member is not associated with an underlying value if its value is not explicitly specified. This is not the case; all members of an enum are associated with an underlying value. The rules for the 'implicit' associations are given by (from the language specification):
• If the enum member is the first enum
member declared in the enum type, its
associated value is zero.
• Otherwise,
the associated value of the enum
member is obtained by increasing the
associated value of the textually
preceding enum member by one. This
increased value must be within the
range of values that can be
represented by the underlying type,
otherwise a compile-time error occurs.

How to give enum values that are having space

i have to create an enum that contains values that are having spaces
public enum MyEnum
{
My cart,
Selected items,
Bill
}
This is giving error. Using concatenated words like MyCart or using underscore My_Cart is not an option. Please guide.
Thanks in advance.
From
enum (C# Reference)
An enumerator may not contain white
space in its name.
Enum just cant have space!
What do you need it for? If you need it simply for display purpose, you can stick with underscore and write an extension method for your enum so that you can ask for the display text by doing this (assuming your ext method is call DisplayText). Internally you just implement the DisplayText method to substitute "_" with space
MyEnum.My_Cart.DisplayText(); // which return "My Cart"
As per the C# specification, "An enumerator may not contain white space in its name."
(see http://msdn.microsoft.com/en-us/library/sbbt4032.aspx)
Why do you need this?
**
enums can't have spaces in C#!" you say. Here is the way
System.ComponentModel.DescriptionAttribute to add a more friendly
description to the enum values. The example enum can be rewritten like
**
public enum DispatchTypes
{
Inspection = 1,
LocalSale = 2,[Description("Local Sale")]
ReProcessing=3,[Description("Re-Processing")]
Shipment=4,
Transfer=5
}
Hence it returns "LocalSale" or "ReProcessing", when we use .ToString()
I agree the use of DisplayText if you are following convention. But if you need different display value to represent the enum constant, then you could have a constructor passing that value.
public enum MyEnum
{
My cart ("Cart"),
Selected items("All Selected Items"),
Bill("Payment");
private String displayValue;
private MyEnum(String displayValue) {
this.displayValue = displayValue;
}
public String displayText() {
return this.displayValue;
}
}
You can have a displayText method or a toString method which would return the displayValue

Categories

Resources