String to enum conversion in C# - 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.

Related

Convert enum to string having a space

I have an enum which I want to present as string using special way:
public enum FailureDescription
{
MemoryFailure,
Fragmentation,
SegmentationFault
}
I want to print the value of that enum as following : FailureDescription.MemoryFailure.ToString() - > Memory Failure
Can I do that ? How? Implement ToString?
You can write own extension method:
public static string ToFormattedText(this MyEnum value)
{
var stringVal = value.ToString();
var bld = new StringBuilder();
for (var i = 0; i < stringVal.Length; i++)
{
if (char.IsUpper(stringVal[i]))
{
bld.Append(" ");
}
bld.Append(stringVal[i]);
}
return bld.ToString();
}
If you want method available for all enums, just replace MyEnum with Enum.
Usage:
var test = MyEnum.SampleName.ToFormattedText();
Consider caching - building string everytime could not be efficient.
Use the Description attribute to decortate your enumeration values. I'd suggest adding a resx file for resources so that you can localise more easily. If you hardcoded "Memory Failure", it becomes more work to be able to change that to another language (as Hans Passant mentioned in the comments).
public enum FailureDescription
{
[Description("Memory Failure")] //hardcoding English
MemoryFailure,
[Description(MyStringsResourceFile.FragmentationDescription)] //reading from a resx file makes localisation easier
Fragmentation,
[Description(MyStringsResourceFile.SegmentationFaultDescription)]
SegmentationFault
}
You can then create a method, or extension method (as shown) to read the Description value.
public static class Extensions
{
public static string GetDescription(this 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();
}
}
Then call the method like so
Console.WriteLine(FailureDescription.MemoryFailure.GetDescription());
This extension method will do it for you:
public static string ToFormattedText(this FailureDescription value)
{
return new string(value.ToString()
.SelectMany(c =>
char.IsUpper(c)
? new [] { ' ', c }
: new [] { c })
.ToArray()).Trim();
}
You also can use a simple regex & linq mixture to extract and concatenate the words:
var withSpaces =
Regex
.Matches(
FailureDescription.MemoryFailureTest.ToString(),
#"([A-Z][a-z]+)(?=[A-Z]|$)")
.Cast<Match>()
.Select(m => m.Groups[1].Value)
.Aggregate((str, next) => (str = str + " " + next));
DEMO at ideone.com
where:
([A-Z][a-z]+)(?=[A-Z]|$)
matches words that begin with upper-case letters until the next upper-case letter or the end of string is found: DEMO at regex101
.Select(m => m.Groups[1].Value)
selects the matched values from the group 1
.Aggregate((str, next) => (str = str + " " + next));
concatenates words and inserts a space between them.
Here is one of the utilities I've been using. #HansPassant in his comment raised a good point about localizing.
This code takes Resource files into consideration. In the attribute with two params first param is the Key in Resource file, where as the second param is the namespace for the resource.
Checkout the git repo https://github.com/seanpaul/EnumExtensions
public enum TestEnum
{
//You can pass what ever string value you want
[StringValue("From Attribute")]
FromAttribute = 1,
//If localizing, you can use resource files
//First param is Key in resource file, second is namespace for Resources.
[StringValue("Test", "EnumExtensions.Tests.Resources")]
FromResource = 2,
//or don't mention anything and it will use built-in ToString
BuiltInToString = 3
}
[Test ()]
public void GetValueFromAttribute ()
{
var testEnum = TestEnum.FromAttribute;
Assert.AreEqual ("From Attribute", testEnum.GetStringValue ());
}
[Test ()]
public void GetValueFromResourceFile ()
{
var testEnum = TestEnum.FromResource;
Assert.AreEqual ("From Resource File", testEnum.GetStringValue ());
}
An elegant solution following the DRY and KISS principles would be using Humanizer:
"Memory Failure".DehumanizeTo<EnumUnderTest>(); // Returns FailureDescription.MemoryFailure.
"Fragmentation".DehumanizeTo<EnumUnderTest>(); // Returns FailureDescription.Fragmentation.
"Segmentation Fault".DehumanizeTo<EnumUnderTest>(); // Returns FailureDescription.SegmentationFault.

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.

Dynamic json object with numerical keys

I have a json object which I converted to dynamic C# object with help of this answer. It works just fine, but trouble is that this object has numerical keys. For instance,
var jsStr = "{address:{"100": {...}}}";
So I can't wirte
dynObj.address.100
And, as I know, I can't use indexers to get this object like this
dynObj.address["100"]
Please explain to me how I can get this working.
As far as I can see from the source code he resolves the properties through a private dictionary, so you have to use reflection to access the dictionary key, or modify his code a bit so that TryGetMember in DynamicJSONObject is the following (and use __numeric__ to get the key e.g. data.address.__numeric__100, and then avoid using __numeric__ as a key):
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
var name = binder.Name;
//Code to check if key is of form __numeric__<number> so that numeric keys can be accessed
if (binder != null && binder.Name != null && binder.Name.StartsWith("__numeric__"))
{
name = binder.Name.Substring(11);
}
if (!_dictionary.TryGetValue(name, out result))
{
// return null to avoid exception. caller can check for null this way...
result = null;
return true;
}
var dictionary = result as IDictionary<string, object>;
if (dictionary != null)
{
result = new DynamicJsonObject(dictionary);
return true;
}
var arrayList = result as ArrayList;
if (arrayList != null && arrayList.Count > 0)
{
if (arrayList[0] is IDictionary<string, object>)
result = new List<object>(arrayList.Cast<IDictionary<string, object>>().Select(x => new DynamicJsonObject(x)));
else
result = new List<object>(arrayList.Cast<object>());
}
return true;
}
My opensource framework ImpromptuInterface has methods to call dynamic members via string name of any C# 4 dynamic object.
object tOut =Impromptu.InvokeGet(dynObj.address,"100");
I tested it with an ExpandoObject it seemed to work just fine.
An identifier must start with a
letter, underscore (_), or dollar sign
($); subsequent characters can also be
digits (0-9). Because JavaScript is
case sensitive, letters include the
characters "A" through "Z" (uppercase)
and the characters "a" through "z"
(lowercase). Starting with JavaScript
1.5, ISO 8859-1 or Unicode letters (or \uXXXX Unicode escape sequences) can
be used in identifiers.
Quoted from: http://en.wikipedia.org/wiki/JavaScript_syntax#Variables
Oh I am sorry mis understood the question, well here you go with a working example you can adjust to your needs:
<script>
var jsStr = {address:{'100': 'test'}};
var test = jsStr.address;
console.log(test);
alert(test[100]);
</script>
btw key CAN be numeric (as you see in the example in the answer), only the identifiers cannot. so you have to access just like you tried. you only have to leave away the quotes for numeric keys! and your json string will not be an object without evaluation, so in this example its strictly speaking a javascript object and not json but it doesnt matter to t he subject

String Parsing in C#

What is the most efficient way to parse a C# string in the form of
"(params (abc 1.3)(sdc 2.0)(www 3.05)....)"
into a struct in the form
struct Params
{
double abc,sdc,www....;
}
Thanks
EDIT
The structure always have the same parameters (same names,only doubles, known at compile time).. but the order is not granted.. only one struct at a time..
using System;
namespace ConsoleApplication1
{
class Program
{
struct Params
{
public double abc, sdc;
};
static void Main(string[] args)
{
string s = "(params (abc 1.3)(sdc 2.0))";
Params p = new Params();
object pbox = (object)p; // structs must be boxed for SetValue() to work
string[] arr = s.Substring(8).Replace(")", "").Split(new char[] { ' ', '(', }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < arr.Length; i+=2)
typeof(Params).GetField(arr[i]).SetValue(pbox, double.Parse(arr[i + 1]));
p = (Params)pbox;
Console.WriteLine("p.abc={0} p.sdc={1}", p.abc, p.sdc);
}
}
}
Note: if you used a class instead of a struct the boxing/unboxing would not be necessary.
Depending on your complete grammar you have a few options:
if it's a very simple grammar and you don't have to test for errors in it you could simply go with the below (which will be fast)
var input = "(params (abc 1.3)(sdc 2.0)(www 3.05)....)";
var tokens = input.Split('(');
var typeName = tokens[0];
//you'll need more than the type name (assembly/namespace) so I'll leave that to you
Type t = getStructFromType(typeName);
var obj = TypeDescriptor.CreateInstance(null, t, null, null);
for(var i = 1;i<tokens.Length;i++)
{
var innerTokens = tokens[i].Trim(' ', ')').Split(' ');
var fieldName = innerTokens[0];
var value = Convert.ToDouble(innerTokens[1]);
var field = t.GetField(fieldName);
field.SetValue(obj, value);
}
that simple approach however requires a well conforming string or it will misbehave.
If the grammar is a bit more complicated e.g. nested ( ) then that simple approach won't work.
you could try to use a regEx but that still requires a rather simple grammar so if you end up having a complex grammar your best choice is a real parser. Irony is easy to use since you can write it all in simple c# (some knowledge of BNF is a plus though).
Do you need to support multiple structs ? In other words, does this need to be dynamic; or do you know the struct definition at compile time ?
Parsing the string with a regex would be the obvious choice.
Here is a regex, that will parse your string format:
private static readonly Regex regParser = new Regex(#"^\(params\s(\((?<name>[a-zA-Z]+)\s(?<value>[\d\.]+)\))+\)$", RegexOptions.Compiled);
Running that regex on a string will give you two groups named "name" and "value". The Captures property of each group will contain the names and values.
If the struct type is unknown at compile time, then you will need to use reflection to fill in the fields.
If you mean to generate the struct definition at runtime, you will need to use Reflection to emit the type; or you will need to generate the source code.
Which part are you having trouble with ?
A regex can do the job for you:
public Dictionary<string, double> ParseString(string input){
var dict = new Dictionary<string, double>();
try
{
var re = new Regex(#"(?:\(params\s)?(?:\((?<n>[^\s]+)\s(?<v>[^\)]+)\))");
foreach (Match m in re.Matches(input))
dict.Add(m.Groups["n"].Value, double.Parse(m.Groups["v"].Value));
}
catch
{
throw new Exception("Invalid format!");
}
return dict;
}
use it like:
string str = "(params (abc 1.3)(sdc 2.0)(www 3.05))";
var parsed = ParseString(str);
// parsed["abc"] would now return 1.3
That might fit better than creating a lot of different structs for every possible input string, and using reflection for filling them. I dont think that is worth the effort.
Furthermore I assumed the input string is always in exactly the format you posted.
You might consider performing just enough string manipulation to make the input look like standard command line arguments then use an off-the-shelf command line argument parser like NDesk.Options to populate the Params object. You give up some efficiency but you make it up in maintainability.
public Params Parse(string input)
{
var #params = new Params();
var argv = ConvertToArgv(input);
new NDesk.Options.OptionSet
{
{"abc=", v => Double.TryParse(v, out #params.abc)},
{"sdc=", v => Double.TryParse(v, out #params.sdc)},
{"www=", v => Double.TryParse(v, out #params.www)}
}
.Parse(argv);
return #params;
}
private string[] ConvertToArgv(string input)
{
return input
.Replace('(', '-')
.Split(new[] {')', ' '});
}
Do you want to build a data representation of your defined syntax?
If you are looking for easily maintainability, without having to write long RegEx statements you could build your own Lexer parser. here is a prior discussion on SO with good links in the answers as well to help you
Poor man's "lexer" for C#
I would just do a basic recursive-descent parser. It may be more general than you want, but nothing else will be much faster.
Here's an out-of-the-box approach:
convert () to {} and [SPACE] to ":", then use System.Web.Script.Serialization.JavaScriptSerializer.Deserialize
string s = "(params (abc 1.3)(sdc 2.0))"
.Replace(" ", ":")
.Replace("(", "{")
.Replace(")","}");
return new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize(s);

Associating Strings with enums in 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.

Categories

Resources