how do i compare a string to an enum - c#

I have a problem where i get some status codes in string format, and after som datahandling, i have to persist
those status codes also as strings.
Example of statuscodes is "0", "3"
But in between retrieving and saving data i have to handle the data in code. I also want my code to make sence to other members of my team, and future programmers. For that ive created an Enum with 4 statuscodes written in words.
Example of statuscodes enum. Resigned, Active.
Now enums are integers, so i cannot switch on the string statuscode and compare with enumvalues like ex.
// object.statuscode is a string.
switch(object.statuscode){
case Enums.Statuscodes.Resigned:
.
.
.
case Enums.Statuscodes.Active:
.
.
.
}
I am not in control of dataformat, but just been given the task of making the code more readable with enums.
Is there a way around this.
Ive tried something like
[EnumMember(Value = "0")]
Resigned
.
.
.
and then
case Enums.Statuscodes.Resigned:
But that does not work.
Does anybody have an idea if this is possible or do i have to suggest that data should be retrieved and stored differently for this to work.

An option would be to layout your enums similar to the receiving status codes and cast them appropriately.
enum StatusCode
{
Resigned = 0,
Active = 3,
...
Undefined = 99999
}
Then, when retrieving the data as string, you have multiple options. One would be to double-cast it into the enum, like phuzi mentioned:
string statusCodeString;
StatusCode result = StatusCode.Undefined;
if (int.TryParse(statusCodeString, out int statusCodeInt))
{
result = (StatusCode)statusCodeInt;
}
An alternative would be to buildup a dictionary beforehand, based on the enum values. This allows for a more direct "cast" and catches issues that are not being caught by the code above:
// This should be a static member somewhere
Dictionary<string, StatusCode> stringToStatusCode = new();
StatusCode[] allStatusCodes = (StatusCode[])Enum.GetValues(typeof(StatusCode));
foreach (StatusCode statusCode in allStatusCodes)
{
stringToStatusCode.Add(((int)statusCode).ToString(), statusCode);
}
Now, you should be able to check against the dictionary. If the key is not in the dictionary, the code is undefined:
string statusCodeString;
if (!stringToStatusCode.TryGetValue(statusCodeString, out result))
result = StatusCode.Undefined;

I would do simple method if you have small count of enums, otherwise approache from Max Play with Dictionary is great option too
enum StatusCode
{
Resigned = 0,
Active = 3,
Undefined = 999
}
public static StatusCode GetStatusCode(string code)
{
switch (code)
{
case "0": return StatusCode.Resigned;
case "3": return StatusCode.Active;
default:
throw new Exception($"Not valid status code: {code}");
//return StatusCode.UNDEFINED;
}
}
//Example
if (GetStatusCode(object.statuscode) == StatusCode.Resigned)
{
//Do what you want
}

Related

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

C# gets an enum, but doesn't work correctly with switch statement

Im having some trouble with enums and then checking it with a switch statement. I'm not sure if I'm doing something super wrong, but I don't get any errors so hey.
# Level.cs
enum LevelID
{
Level_One = 0,
Level_Two = 1,
}
private LevelID lvlID;
private string xml;
StreamReader xmlStream;
public Level()
{
switch (lvlID)
{
case LevelID.Level_One:
xmlStream = new StreamReader("../../assets/xml/test1.tmx");
break;
case LevelID.Level_Two:
xmlStream = new StreamReader("../../assets/xml/test2.tmx");
break;
}
xml = xmlStream.ReadToEnd();
xmlStream.Close();
}
public LevelID LvlID
{
get { return this.lvlID; }
set { this.lvlID = value; }
}
public string Xml
{
get { return this.xml; }
}
## The xml gives me lots of numbers from the different files
# game.cs
Level lvl = new Level();
lvl.LvlID = LevelID.Level_Two;
## When I then get the lvlID from Level.cs again its says it holds Level_Two,
## but the switch still gives me the numbers from test1.xml
The thing is that I'm checking with an enum in a switch statement to determine what xml file I should store in a String that I parse into an XElement.
When I try to do this it always gives me the xml from the first switch case, even though that when I check, my enum variable holds the correct enum for the second case. Even without a default in my switch it still loads the first case's xml file. What is going on and what am I doing wrong?
So your switch is in the constructor. It's only evaluated when you instantiate the class. Since you're setting the property after that happens, the switch never sees it.
That said, this is something that could be trivially seen if you used your debugger. You could stop the code at the switch statement and see what the value was. Then you could step through the code to see why the value was not what you expected.

can I use a c# switch here?

i would like to refactor this code. Maybe if possible by using a switch? Or is it the same in terms of performance?
string rawUrl = context.Request.RawUrl ?? string.Empty;
if (rawUrl.Contains("mypage.aspx"))
{
}
if (rawUrl.Contains("mypage2.aspx"))
{
}
etc..
Not directly, since you want a "contains" relation, rather than an exact equality.
However, if you so desire, you could do it indirectly by attempting to parse the page name out of what I assume would be the URL, storing it in a separate String variable, and switching on that String.
For example:
// Get the URL from some external source (wherever you're already getting it from)
String rawUrl = "http://www.example.com/foo/bar.aspx";
// Means of parsing will be dependent on the format in which you expect the URL.
String page = rawUrl.Substring(rawUrl.LastIndexOf("/") + 1);
switch (page) {
case "bar.aspx":
// Do stuff
break;
case "foo.aspx":
// Do stuff
break;
}
And, of course, please take this parsing methodology with a grain of salt; this example was to show you that it is possible, but note that this method of parsing could potentially throw an exception in a number of cases, but I've omitted those checks for the sake of brevity.
Switch Cases must be a constant value. You're best bet there is to use if/else like so:
string rawUrl = context.Request.RawUrl ?? string.Empty;
if (rawUrl.Contains("mypage.aspx"))
{
//code
}
else if (rawUrl.Contains("mypage2.aspx"))
{
//more code
}
If you're concerned about performance (which is good!) then the else is the way to go. While not using an else will have the same functionality, by adding the else, you're telling the code to not process any of the other if conditions. So 10 if statements will result in 10 if conditions being processed not matter what, while 10 if/else statements might result in 10, or it might only result in 1.
EDIT:
Thought about this some, and I noticed you were using the context object. If you really wanted a switch statement, you can do the following:
string page = context.Request.Url.Segments.Last();
switch(page)
{
case "mypage.aspx":
//code
break;
case "mypage2.aspx":
//more code
break;
}
Not for a contains.
Try to isolate page name alone and you can could do it.
switch(pageName)
{
case "mypage.aspx";
break;
case "mypage2.aspx";
break;
}
I think it is better to use a Dictionary.
First, extract the file name from the raw url.
Then, use a Dictionary<string,TValue>.
If the actions to the pages are almost the same, set TValue to the type of the data associated with the pages.
If the actions are very different, set TValue to a delegate type such as Action.

How to identify given int value belongs to which enum

Hi before going to direct problem let me show my code :
//Definition of enum
public enum LogType
{
Warning = -2,
Error = -1,
Info = 0,
EruCtorDtor = 1,
Notifications = 2,
CommunicationWithAOT = 4,
ExecutedOrder = 8,
ERUInfo = 16,
DebugLog = 32,
}
//Use of enum
CurrentLogFlagSettings = nLogFlag;
LogFlagMap = new SortedDictionary<LogType, int>();
ulong mask = 1;
while(mask <= nLogFlag)
{
if ((nLogFlag & mask) == mask)
{
LogType type = (LogType)mask; //Step 1
string val = type.ToString(); //Step 2
//Processing the value
LogFlagMap.Add(type, tempVal)
LogMsg(val + " added", type);
}
mask <<= 1;
}
What I want is : Process step2 only after step1 has produced valid value. I mean value should be between range defined in enum definition. Otherwise I dont want to process it.
for e.g.
case 1 - Lets say mask value is 32,
its defined in enum. So type is
getting value DebugLog and so it
type.ToString() (i.e. "DebugLog"),
this is a valid case.
case 2- Lets
say mask value is 128 and its not
defined in enum, in this case I dont
want to process anything on 128
value. But what is happening its
geting value 128 in type and
type.ToString() is converting it
into 128. I dont want this, I want
to make sure whether 128 belongs to
enum values or not.
I want to prevent 2nd case to be executed. Is there any solution for my problem?
Please let me know if more details are needed.
You could use Enum.IsDefined, like so:
int value = 128;
Console.WriteLine(Enum.IsDefined(typeof(LogType), value)); // will print out False
Firstly, let me seriously apologise, Ive had like no sleep, so if I missed the point a little. Please, just ignore me.
You can enumerate your LogType with Enum.GetValues(typeof(LogType))), so you could step through and check a value against it. I had some code, but, I couldnt promise it compiled.
Bool isValid(int i)
{
foreach (LogType l in Enum.GetValues(typeof(LogType)))
{
if ((int)l == i) return true;
}
return false;
}
You can also use Enum.GetValues(typeof(LogType)) to get all the possible values for your enum and do what you want through that.
i.e.
var values = Enum.GetValues(typeof (LogType));
foreach (LogType type in values)
{
if (((int)type & nLogMask) == (int)type)
{
//value is valid, process the value
}
}
One addition to your code could be the addition of the [Flags] attribute to you enum, this then makes it clear that the enum values are for bitwise operations
e.g.
[Flags]
public enum LogType
{
Warning = -2,
Error = -1,
Info = 0,
EruCtorDtor = 1,
Notifications = 2,
CommunicationWithAOT = 4,
ExecutedOrder = 8,
ERUInfo = 16,
DebugLog = 32,
}
although to do this, you would need to change the values such that the Warning and Error take the top 2 bits of the enum value (assuming this is still necessary).
The c# Enum class also has the method GetName(). This might provide a nice and easy manner to retrieve the name of the value set
e.g.
Enum.GetName( typeof(LogType), 4 ); // result = CommunicationWithAOT
I have a library called Unconstrained Melody which allows you to express all of this in a type-safe generic way and avoids boxing too. Personally I prefer that over using Enum.IsDefined, but obviously that doesn't involve learning an extra library.
It's probably not worth using Unconstrained Melody if this is the only thing you need to do with your enum, but if you've got other similar operations, you may wish to consider it.

C# using numbers in an enum

This is a valid enum
public enum myEnum
{
a= 1,
b= 2,
c= 3,
d= 4,
e= 5,
f= 6,
g= 7,
h= 0xff
};
But this is not
public enum myEnum
{
1a = 1,
2a = 2,
3a = 3,
};
Is there a way I can use an number in a enum? I already have code that would populate dropdowns from enums so it would be quite handy
No identifier at all in C# may begin with a number (for lexical/parsing reasons). Consider adding a [Description] attribute to your enum values:
public enum myEnum
{
[Description("1A")]
OneA = 1,
[Description("2A")]
TwoA = 2,
[Description("3A")]
ThreeA = 3,
};
Then you can get the description from an enum value like this:
((DescriptionAttribute)Attribute.GetCustomAttribute(
typeof(myEnum).GetFields(BindingFlags.Public | BindingFlags.Static)
.Single(x => (myEnum)x.GetValue(null) == enumValue),
typeof(DescriptionAttribute))).Description
Based on XSA's comment below, I wanted to expand on how one could make this more readable. Most simply, you could just create a static (extension) method:
public static string GetDescription(this Enum value)
{
return ((DescriptionAttribute)Attribute.GetCustomAttribute(
value.GetType().GetFields(BindingFlags.Public | BindingFlags.Static)
.Single(x => x.GetValue(null).Equals(value)),
typeof(DescriptionAttribute)))?.Description ?? value.ToString();
}
It's up to you whether you want to make it an extension method, and in the implementation above, I've made it fallback to the enum's normal name if no [DescriptionAttribute] has been provided.
Now you can get the description for an enum value via:
myEnum.OneA.GetDescription()
No, there isn't. C# does not allow identifiers to start with a digit.
Application usability note: In your application you should not display code identifiers to the end-user anyway. Think of translating individual enumeration items into user-friendly displayable texts. Sooner or later you'll have to extend the enum with an item whose identifier won't be in a form displayable to the user.
UPDATE: Note that the way for attaching displayable texts to enumeration items is being discusses, for example, here.
An identifier in C# (and most languages) cannot start with a digit.
If you can modify the code that populates a dropdown with the enumeration names, you could maybe have a hack that strips off a leading underscore when populating the dropdown and define your enum like so:
public enum myEnum
{
_1a = 1,
_2a = 2,
_3a = 3
};
Or if you don't like the underscores you could come up with your own 'prefix-to-be-stripped' scheme (maybe pass the prefix to the constructor or method that will populate the dropdown from the enum).
Short and crisp 4 line code.
We simply use enums as named integer for items in code,
so any simplest way is good to go.
public enum myEnum
{
_1 = 1,
_2,
_3,
};
Also for decimal values,
public enum myEnum
{
_1_5 = 1,
_2_5,
_3_5,
};
So while using this in code,
int i = cmb1.SelectedIndex(0); // not readable
int i = cmb1.SelectedIndex( (int) myEnum._1_5); // readable
No way. A valid identifier (ie a valid enumeration member) cannot start with a digit.
Enumerations are no different than variables in terms of naming rules. Therefore, you can't start the name with a number. From this post, here are the main rules for variable naming.
The name can contain letters, digits, and the underscore character
(_).
The first character of the name must be a letter. The underscore is
also a legal first character, but its
use is not recommended at the
beginning of a name. An underscore is
often used with special commands, and
it's sometimes hard to read.
Case matters (that is, upper- and lowercase letters). C# is
case-sensitive; thus, the names count
and Count refer to two different
variables.
C# keywords can't be used as variable names. Recall that a keyword
is a word that is part of the C#
language. (A complete list of the C#
keywords can be found in Appendix B,
"C# Keywords.")
Identifiers can't start with numbers. However, they can contain numbers.
Here is what i came up with as an alternative, where I needed Enums to use in a "for" Loop and a string representation equivalent to use in a Linq query.
Create enums namespace to be used in "for" Loop.
public enum TrayLevelCodes
{
None,
_5DGS,
_5DG,
_3DGS,
_3DG,
_AADC,
_ADC,
_MAAD,
_MADC
};
Create strings based on enum created to be used for Linq query
public string _5DGS = "\"5DGS\"",
_5DG = "\"5DG\"",
_3DGS = "\"3DGS\"",
_3DG = "\"3DG\"",
_AADC = "\"AADC\"",
_ADC = "\"ADC\"",
_MAAD = "\"MAAD\"",
_MADC = "\"MADC\"";
Create function that will take an enum value as argument and return corresponding string for Linq query.
public string GetCntnrLvlDscptn(TrayLevelCodes enumCode)
{
string sCode = "";
switch (enumCode)
{
case TrayLevelCodes._5DGS:
sCode = "\"5DGS\"";
break;
case TrayLevelCodes._5DG:
sCode = "\"5DG\"";
break;
case TrayLevelCodes._3DGS:
sCode = "\"3DGS\"";
break;
case TrayLevelCodes._3DG:
sCode = "\"3DG\"";
break;
case TrayLevelCodes._AADC:
sCode = "\"AADC\"";
break;
case TrayLevelCodes._ADC:
sCode = "\"AAC\"";
break;
case TrayLevelCodes._MAAD:
sCode = "\"MAAD\"";
break;
case TrayLevelCodes._MADC:
sCode = "\"MADC\"";
break;
default:
sCode = "";
break;
}
return sCode;
}
Here is how i am using what i created above.
for (var trayLevelCode = TrayLevelCodes._5DGS; trayLevelCode <= TrayLevelCodes._MADC; trayLevelCode++)
{
var TrayLvLst = (from i in pair1.Value.AutoMap
where (i.TrayLevelCode == HTMLINFO.GetCntnrLvlDscptn(trayLevelCode))
orderby i.TrayZip, i.GroupZip
group i by i.TrayZip into subTrayLvl
select subTrayLvl).ToList();
foreach (DropShipRecord tray in TrayLvLst)
{
}
}

Categories

Resources