I have a marital status field in a database with numbers each representing a marital status in a different table. The first table has numbers 1, 3, 4:
1 = single
3 = married
4 = divorced
The second has the values:
single
married
divorced
I need an if statement as follows: if the table field returns the value 1 I want to write the message 'single', if 3 'married' and if 4 'divorced' (using Response.Write).
Rather than write an "if" statement in C#, why not just perform a join in the database to retrieve the text as part of the database query?
You can write if else, or case statement, but easier to just use array for such a small list
string[] maritalStatus = new string[] { "", "Single", "", "Married", "Divorced"};
Response.Write (maritalStatus[dbValue]);
Another option would be to declare an enum that maps to your values like this:
enum RelationshipStatus
{
Single = 1,
Married = 3,
Divorced = 4
}
Then you can just call the ToString method of the variable containing your enum value. Note that you'll need to cast the integer value from your SQL table to the enum type.
The advantage of this approach is that you can eliminate the conditional if statement entirely and I think the result is much more readable. The disadvantage is that it won't be easy to localize to other languages (translations) if you need to.
string GetStatus(int StatusCode)
{
return StatusCode == 1 ? "single" : StatusCode == 3 ? "married" : "divorced";
}
BTW there is usually no good reason to normalize this far. Performance would be better if you stored the actual status in the table and the amount of disk space is negligible.
Edit: #jon's idea is probably better than this function
Perhaps an Enum would be the most elegant approach?
public enum MaritalStatus
{
Single = 1,
Married = 3,
Divorced = 4
}
Then you can simply use Enum.GetName() and Enum.Parse() to convert between string, int and MaritalStatus objects. This may also provide you with a more elegant solution in some of your other business logic.
There are number of different ways to do that, good and bad ways
If you can join the tables in query - there's one good way
You could use SQL-CASE in procs
You could switch-case/if in middle tier/C# code.
You could create enums and type cast
like
`
public enum MaritalStatus
{
Single = 1,
Married = 3,
Divorced = 4
}
MaritalStatus status = (MaritalStatus) 4;
Console.WriteLine(status);
`
and so on
Related
I have enums GroupTypes which I add to Types property.
There can be either Up/Down or both Up And Down types.
This is my code:
public enum GroupTypes: int {
Down = 0,
Up = 1,
}
[Column("types")]
[JsonProperty(PropertyName = "types")]
public GroupTypes[] Types {
get;
set;
}
// e.g.
var group = new Group() {
Name = "Group",
Types = new GroupTypes[] {
GroupTypes.Up
},
OrganizationId = Organization.Id,
};
However when I post them to database I get 500 error.
It seems correct to me and I am not sure what goes wrong.
In my database the type for this column is: "types" int4[] NULL and I expect them to be saved as array ( [0,1] or [1] or [0])in the database . What I am misisng here?
Before adding types, post method worked fine so I am assuming they are the problem but it seems like I define them correctly. I am using DBeaver and PostgreSQL
It is usually a good idea to use Flags enum & bitwise operations
Your case should be like this:
[Flags]
public enum GroupTypes:
{
None = 0,
Down = 1,
Up = 2,
DownAndUp = Down | Up
}
As a result, You need to save single value only instead of list of values.
If I were you, I wouldn't use an array to reflect that model. It'd be much better to use Up, Down and Both, for instance, otherwise you have to deal with the fact that it could be [1], [0], [1, 0], [0, 1], four combinations for the three states you need to present.
Also, this would map more elegantly to a database. In fact, I pretty much would use a character or even a whole string to represent that data, which gives more semantic value in the database (you already calling the column type, which is pretty generic.
public enum Types : char
{
Down = 'D',
Up = 'U',
Both = 'B',
}
With all that said, I haven't answered your question, because I need more information on the error you're getting and how you are communicating with the database.
I have a enum list of Parts:
public enum Parts
{
Wall = (int)'#',
Empty = (int)'-',
Player = (int)'#',
Goal = (int)'.',
Block = (int)'$',
BlockOnGoal = (int)'*',
PlayerOnGoal = (int)'+'
};
And I have a string
string Level = "####\n# #\n## #\n####";
so all of the items in the string are in the enum list
If i then call the function
public Parts WhatsAt(int row, int column)
{
return Parts.Wall;// Teachers comment but idk what he means "WRONG - but now no exception! should look up Level or some better data structure"
}
so when that function is called it will split the string on \n and then use the inputs into the function like a co-ordinates on a map to pinpoint a specific character in it.
Top left is row: 0, column: 0
bottom right is row: 4, column: 4
####
# #
## #
####
How do I then make the function return what Part name is at a specified giving co-ordinate.
e.g.
Parts actual = WhatsAt(0, 2);
actual would then become Wall as that's what # means in the enum list
Thanks for any help in advance
If I got you right, you can try this:
public static Parts WhatsAt(int row, int column)
{
var rows = Level.Split('\n');
return (Parts)rows[row][column];
}
Have you tried anything? If you can't get even far enough to make an attempt to write some code, you will be much better served by discussing with your teacher, as it means you've missed some critical concept that he was trying to convey.
In the meantime:
There are really two parts (at least) to your question:
How to find the char value for a given row and column value.
How to convert a char value to the Parts enum value. (Aside: it is a .NET coding convention to make enum type names singular, not plural, unless the enum has the [Flags] attribute, and this convention is IMHO a useful one. So Part would be a better name for your enum type than Parts).
On the first point, as you seem to already be aware, you can use the string.Split() method to obtain an array of string values from your original:
string[] rows = Level.Split('\n');
You can index this array with the row variable passed in to obtain the correct string value for that row. Within that string value, you can then user the column variable to index the desired char value for the location:
char location = rows[row][column];
On the second point, converting back to the Parts type is a simple cast:
Parts part = (Parts)location;
This does not change the actual value being used, but rather causes the location value to be reinterpreted as a Parts value, stored in the part variable.
And with that, you should have everything you need to write your WhatsAt() method. All of the above can even be expressed as a single expression if you like:
(Parts)Level.Split('\n')[row][column]
I need to convert a List of enums values to a single string to store in my db; then convert back again when I retrieve from the database.
Each enum's value is currently a simple integer, so it feels a bit overkill to create an extra table to deal with this.
So, in the example below, if the user selects Mother, Father and Sister, then the value stored in the database will be "0,1,3"
public enum MyEnum
{
Mother = 0,
Father = 1,
Gran = 2,
Sister = 3,
Brother = 4
}
I'm new to c#, so not sure if there is a nice out-the-box way to do this - I couldn't find anything obvious when google hunting!
Cheers in advance :)
- L
Enum's are explicitely able to be cast to/from integers
int value = (int)MyEnum.Mother;
and
MyEnum value = (MyEnum)1;
For strings use ToString and Enum.Parse
string value = MyEnum.Mother.ToString();
and
MyEnum value = (MyEnum)Enum.Parse(typeof(MyEnum),"Mother");
If you change you enum values to:
[Flags]
public enum MyEnum
{
Mother = 1,
Father = 2,
Gran = 4,
Sister = 8,
Brother = 16,
}
Then you could store Father and Gran as 6
Sister and Brother as 24 etc
by using binary numbers you should not get duplicate values by combining them
The following will convert back and forth between an array of Enum values via "0,1,3" as requested:
MyEnum[] selection = { MyEnum.Mother, MyEnum.Father, MyEnum.Sister };
string str = string.Join(",", selection.Cast<int>());
MyEnum[] enm = str.Split(',').Select(s => int.Parse(s)).Cast<MyEnum>().ToArray();
from your code it is
MyEnum a = MyEnum.Mother;
string thestring = a.ToString();
MyEnum b = (MyEnum) Enum.Parse(typeof(MyEnum), thestring);
Just use ToString to convert to the name, and the use Enum.TryParse (or Enum.Parse if you're not on .NET 4) to convert back.
If you're wanting one enum field to contain multiple values (e.g MyEnum.Mother | MyEnum.Father), you'll need to convert that to a Flags enum, as #WraithNath suggested. Otherwise you're talking about storing each option separately (there's no way to store Mother and Father in the same field with your current setup).
String Equivelant
MyEnum value = MyEnum.Father;
value.ToString(); // prints Father
Parsing
(MyEnum)Enum.Parse(typeof(MyEnum), "Father"); // returns MyEnum.Father
Enums can be cast to and from integer values. That's probably your best bet.
If you really want to use strings, ToString will return "Mother" "Father" "Gran" etc. Casting back from a string would just be a function:
private MyEnum GetEnumValue(string fromDB)
{
if( fromDB == "Mother" ) return MyEnum.Mother;
else if( fromDB == "Father") return MyEnum.Father;
//etc. etc.
}
EDIT:
Ian's answer for casting back is the more "C#-ey" way of doing it, and is far more extensible (handles adding new values to the enum much more flexibly).
Further to Jamiec's suggestion (which fits well for your need), if you need to defensively code your casting, try:
if (Enum.IsDefined(typeof(MyEnum), <your database value>))
{
// Safe to convert your enumeration at this point:
MyEnum value = (MyEnum)1;
}
I have a scenario where I'm using a Dictionary to hold a list of transaction types that a certain system accepts. The key in the Dictionary is an enum field, the value is an int.
At some point in the system, we're going to want to do something like this:
sqlCommand.Parameters.AddWithValue("#param", LookupDictionary[argument.enumField]);
When we look up the field in the dictionary, we're going to get the correct integer value to feed to the database. I've thought about actually using the enum int value for this, but that's not exactly right. We're interacting with a system where we need to feed a magic number in to represent the kind of update we're doing.
The code above works just fine. I have an initializer method that adds the known types:
LookupDictionary = new Dictionary<mynamespace.myproject.myclass.enumType, int>();
LookupDictionary.Add(enumType.entry1, 4);
LookupDictionary.Add(enumType.entry2, 5);
LookupDictionary.Add(enumType.entry3, 6);
This code also works fine.
But up above, before I actually get in to using the LookupDictionary, I validate that the request being made is actually set to an enum value we support. That's LookupDictionary's main reason to be, it holds the valid ones (there are valid enum entries that this method doesn't work with).
This is the code that doesn't work: the system fails to recognize that the enums match. In the debugger, I can see that the entries list in LookupDictionary does show that it has the value for entry2 - it just calls it like that, entry2. The incoming enumField on the other hand has the full namespace; mynamespace.myproject.myclass.enumType.entry2 - I imagine this is why it doesn't see them as being the same.
if (!LookupDictionary.ContainsKey(argument.enumField))
{
throw new InvalidOperationException("argument.enumField not valid in blahMethod.");
}
Did I mention that this is being passed across a WCF service? But I'm not using an auto-generated proxy ... both projects on both sides of the wire share the types as a project reference, and I build up my channel client in code.
Any ideas? Am I doing it wrong? Do Dictionaries with Enums as keys not work well? Is it a WCF thing?
Note: thanks for the suggestions regarding setting the enums up to contain the magic int. I wanted to set those in a configuration, however, as its possible that the "magic numbers" 4 5 and 6 might change down the road. So if I code them in to the enum as suggested:
public enum MyEnum
{
MyValue1 = 4,
MyValue2 = 5,
MyValue3 = 6
}
I lose the ability to write a method that sets up the magic numbers in the future at run time; instead it would require a code change.
Instead of using the enum as the key, use the integer representation of the enum.
For instance:
LookupDictionary = new Dictionary<int, int>();
LookupDictionary.Add((int)enumType.entry1, 4);
LookupDictionary.Add((int)enumType.entry2, 5);
LookupDictionary.Add((int)enumType.entry3, 6);
That way, you can use the same 'ContainsKey' method of the dictionary. I'm not sure this is much better performance than a List<int>
You shouldn't need a lookup table here at all:
public enum MyEnum
{
MyValue1 = 4,
MyValue2 = 5,
MyValue3 = 6
}
// Sample usage
MyEnum firstEnum = MyEnum.MyValue1;
int intVal = (int)firstEnum; // results in 4
// Enum Validation
bool valid = Enum.IsDefined(typeof(MyEnum), intVal); // results in true
Can you considered typing your enumeration explicitly as int (or whatever the underlying type is) and then setting the value of each of your enumerations to the database value? You've already tightly coupled the enumeration to the database, so either the relationship will be dictated in C# (current hard-coding) or by SQL (perhaps a proc that returns the ID as well as a string that can be parsed into an enumeration.)
Using the assumption that your enumeration is an int...
enum enumType {
entry1 = 4,
entry2 = 5,
entry3 = 6
}
When adding your parameter you would then just cast as the enum's underlying type.
sqlCommand.Parameters.AddWithValue("#param", (int)argument.enumField);
You can explicitly set the values of the enum using the syntax
enum ArgumentTypes {
Arg1 = 1;
Arg2 = 3;
Arg3 = 5;
}
You don't need to keep each value sequential in the enum for this syntax to work.
To validate that only parameters that are valid for the method are ever used, try this sample code. Note I suggest using an ArgumentException over an InvalidOperationException in this context.
public void DoDbWork(ArgumentTypes argType, object otherParameter)
{
if (argType == ArgumentTypes.Arg3) {
throw new ArgumentException("Argument of value " + argType + " is not valid in this context", "argType");
}
// Handle db transaction here
}
To add the int value as the parameter:
cmd.Parameters.AddWithValue("#paramName", (int)argType);
say I have the following declarations:
public enum Complexity { Low = 0, Normal = 1, Medium = 2, High = 3 }
public enum Priority { Normal = 1, Medium = 2, High = 3, Urgent = 4 }
and I want to code it so that I could get the enum value (not the index, like I earlier mentioned):
//should store the value of the Complexity enum member Normal, which is 1
int complexityValueToStore = EnumHelper.GetEnumMemberValue(Complexity.Normal);
//should store the value 4
int priorityValueToStore = EnumHelper.GetEnumMemberValue(Priority.Urgent);
How should this reusable function look like?
tia!
-ren
Revised answer (after question clarification)
No, there's nothing cleaner than a cast. It's more informative than a method call, cheaper, shorter etc. It's about as low impact as you could possibly hope for.
Note that if you wanted to write a generic method to do the conversion, you'd have to specify what to convert it to as well: the enum could be based on byte or long for example. By putting in the cast, you explicitly say what you want to convert it to, and it just does it.
Original answer
What do you mean by "index" exactly? Do you mean the numeric value? Just cast to int. If you mean "position within enum" you'd have to make sure the values are in numeric order (as that's what Enum.GetValues gives - not the declaration order), and then do:
public static int GetEnumMemberIndex<T>(T element)
where T : struct
{
T[] values = (T[]) Enum.GetValues(typeof(T));
return Array.IndexOf(values, element);
}
You can find the integer value of an enum by casting:
int complexityValueToStore = (int)Complexity.Normal;
The most generic way I know of is to read the value__ field using reflection.
This approach makes no assumptions about the enum's underlying type so it will work on enums that aren't based on Int32.
public static object GetValue(Enum e)
{
return e.GetType().GetField("value__").GetValue(e);
}
Debug.Assert(Equals(GetValue(DayOfWeek.Wednesday), 3)); //Int32
Debug.Assert(Equals(GetValue(AceFlags.InheritOnly), (byte) 8)); //Byte
Debug.Assert(Equals(GetValue(IOControlCode.ReceiveAll), 2550136833L)); //Int64
Note: I have only tested this with the Microsoft C# compiler. It's a shame there doesn't appear to be a built in way of doing this.
I realize this isn't what you asked, but it's something you might appreciate.
I discovered that you can find the integer value of an enum without a cast, if you know what the enum's minimum value is:
public enum Complexity { Low = 0, Normal = 1, Medium = 2, High = 3 }
int valueOfHigh = Complexity.High - Complexity.Low;
This wouldn't work with Priority, unless you added some minimal value of 0, or added 1 back:
public enum Priority { Normal = 1, Medium = 2, High = 3, Urgent = 4 }
int valueOfUrgent = Priority.Urgent - Priority.Normal + 1;
I find this technique much more aesthetically appealing than casting to int.
I'm not sure off the top of my head what happens if you have an enum based on byte or long -- I suspect that you'd get byte or long difference values.
If you want the value, you can just cast the enum to int. That would set complexityValueToStore == 1 and priorityValueToStore == 4.
If you want to get the index (ie: Priority.Urgent == 3), you could use Enum.GetValues, then just find the index of your current enum in that list. However, the ordering of the enum in the list returned may not be the same as in your code.
However, the second option kind of defeats the purpose of Enum in the first place - you're trying to have discrete values instead of lists and indices. I'd rethink your needs if that is what you want.
This is the most simple way to solve your problem:
public static void GetEnumMemberValue<T>(T enumItem) where T : struct
{
return (int) Enum.Parse(typeof(T), enumItem.ToString());
}
It works for me.