Associating Strings with enums in C# - c#

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.

Related

How to find .ToString() calls on enum values in C#

I am trying to use Roslyn analyzers to find all instances of using .ToString() on an enum value so that I can suggest a codefix to use the nameof() expression instead. How can I go about doing this? I've trudged through it for the string interpolation scenario in what feels like an overly complicated way, but still need to do it for the other scenarios listed below.
public void PrintStuffOut() {
//all below lines should flag to suggest using nameof(Foo.Bar)
Console.WriteLine(Foo.Bar);
Console.WriteLine(Foo.Bar.ToString());
Console.WriteLine($"Your enum is {Foo.Bar}");
Console.WriteLine($"Your enum is {Foo.Bar.ToString()}");
Console.WriteLine("Your enum is " + Foo.Bar);
Console.WriteLine("Your enum is " + Foo.Bar.ToString());
}
public enum Foo {
Bar
}
Here's what I have so far. This works, but surely this can be done far better
public override void Initialize(AnalysisContext context) {
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.RegisterSyntaxNodeAction(AnalyzeInterpolatedString, SyntaxKind.InterpolatedStringExpression);
}
private void AnalyzeInterpolatedString(SyntaxNodeAnalysisContext ctx) {
var interpolationSyntax = (InterpolatedStringExpressionSyntax)ctx.Node;
foreach (var interpolatedChunk in interpolationSyntax.Contents.OfType<InterpolationSyntax>()) {
var expTypeInfo = ctx.SemanticModel.GetTypeInfo(interpolatedChunk.Expression);
//this if covers the case of when there is an explicit .ToString() on the enum's value in the string interpolation. Feels super hacky.
if (expTypeInfo.Type != null && expTypeInfo.Type.SpecialType == SpecialType.System_String) {
var childrenExpressions = interpolatedChunk.Expression.ChildNodes().OfType<MemberAccessExpressionSyntax>();
foreach (var childExpression in childrenExpressions) {
var childExpTypeInfo = ctx.SemanticModel.GetTypeInfo(childExpression.Expression);
if (childExpTypeInfo.Type != null && childExpTypeInfo.Type.TypeKind == TypeKind.Enum) {
//super hacky way to get the enum name and value. I need to report back EnumName and Value in the example of EnumName.Value.ToString()
ctx.ReportDiagnostic(Diagnostic.Create(Descriptors.UseNameOfForEnum, interpolationSyntax.GetLocation(), interpolatedChunk.Expression.ToString().Split('.').Take(2).ToArray()));
}
}
}
//the else if here covers when there is no explicit .ToString() in the interpolated string
else if (expTypeInfo.Type != null && expTypeInfo.Type.TypeKind == TypeKind.Enum) {
//super hacky way to get the enum name and value. I need to report back EnumName and Value in the example of EnumName.Value.ToString()
ctx.ReportDiagnostic(Diagnostic.Create(Descriptors.UseNameOfForEnum, interpolationSyntax.GetLocation(), interpolatedChunk.Expression.ToString().Split('.')));
}
}
}
I feel like I should be able to do something by registering an action on SyntaxKind.InvocationExpression, checking that the method being invoked is .ToString(), and that it's being invoked on an enum value. Is it possible to do it this way? Would that catch the scenarios above where I'm not explicitly using .ToString()? If not, what's the best way to go about accomplishing this?

C# reflections with enum as string

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);
}

Generics and Types and Arrays

Can a 'generic type' be an array?
And in the cases where it is, how can one access that array?
Can you access a given generic type T as an array, when it is one, and as a non-array when it is not one?
For instance:
If I had a method like ->
void MustBeOfType<T>(T value){}
Can I have ->
MustBeOfType<int>(10);
And Also ->
MustBeOfType<int[]>( new int[] { 1, 2, 3 } );
And within those generic methods, can they access the values as one would expect? - one as an int and one as an int[]?
I think there might be something with typeof(T).IsArray()... but I just can't for the life of me figure out how to cast the parameter as an array when it is one.
Thanks!
You could...but, I'm not sure you should:
void MustBeOfType<T>(T value)
{
Array array = value as Array;
if (array != null) //It is an array
{
foreach (var arrayItem in array)
{
}
for (int i = 0; i < array.Length; i++)
{
var arrayItem = array.GetValue(i);
}
}
else //It is not an array
{
}
}
I am not sure what you are trying to do but Generic types can be anything. You can put restrictions to your generic type with where clause, but this is up to you and up to functionality and context.
Lets take List as example. Let say that we have List inside List. Then we define it as:
List<List<string>> myListOfList = new List<List<string>>();
your must be of type can also be anything ( if you didnt put restriction with where clause)
MustBeOfType<int[][]>()
MustBeOfType<List<List<string>>>()
MustBeOfType<AnyOtherGenericClass<List<string>>>()
and to be able to access it:
class MustBeOfType<T>
{
private T _value;
MustBeofType(T value)
{
_value = value;
}
}
to be able to make operation on , you can use reflection or if you put where restriction and your where restriction has Type, then you can see properties of that Type.

List all bit names from a flag Enum

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.

String to enum conversion in C#

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.

Categories

Resources