I am trying to make an API, one function in that API takes Enum as parameter which then corresponds to a string which is used.
public enum PackageUnitOfMeasurement
{
LBS,
KGS,
};
The trivial method to code this will have to list every case in code. But as their are 30 cases so I am trying to avoid that and use Dictionary Data Structure, but I can't seem to connect the dots on how will I relate value to enum.
if(unit == PackageUnitOfMeasurement.LBS)
uom.Code = "02"; //Please note this value has to be string
else if (unit == PackageUnitOfMeasurement.KGS)
uom.Code = "03";
Here is one way you could store the mapping in a dictionary and retrieve values later:
var myDict = new Dictionary<PackageUnitOfMeasurement,string>();
myDict.Add(PackageUnitOfMeasurement.LBS, "02");
...
string code = myDict[PackageUnitOfMeasurement.LBS];
Another option is to use something like the DecriptionAttribute to decorate each enumeration item and use reflection to read these out, as described in Getting attributes of Enum's value:
public enum PackageUnitOfMeasurement
{
[Description("02")]
LBS,
[Description("03")]
KGS,
};
var type = typeof(PackageUnitOfMeasurement);
var memInfo = type.GetMember(PackageUnitOfMeasurement.LBS.ToString());
var attributes = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute),
false);
var description = ((DescriptionAttribute)attributes[0]).Description;
The benefit of the second approach is that you keep the mapping close to the enum and if either changes, you don't need to hunt for any other place you need to update.
You can specify the number value of the enum elements
public enum PackageUnitOfMeasurement {
None = 0,
LBS = 2,
KGS = 3,
TONS = 0x2a
};
Then you can simply convert the units with
uom.Code = ((int)unit).ToString("X2");
NOTE:
The fundamental question is, whether it is a good idea to hard-code the units. Usually this sort of things should be put into a lookup table in a DB, making it easy to add new units at any time without having to change the program code.
UPDATE:
I added an example containing a HEX code. The format "X2" yields a two digit hex value. Enter all numbers greater than 9 in hex notation like 0xA == 10, 0x10 == 16 in c#.
I would use attributes attached to the enum. Like this:
var type = typeof(PackageUnitOfMeasurement);
var memInfo = type.GetMember(PackageUnitOfMeasurement.LBS.ToString());
var attributes = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute),
false);
var description = ((DescriptionAttribute)attributes[0]).Description;
This is taken from this question, Getting attributes of Enum's value. Which asks specifically about returning Enum attributes.
Oded's solution is fine, but an alternative that I've used in the past is to use attributes attached to the enum values that contain the corresponding string.
Here's what I did.
public class DisplayStringAttribute : Attribute
{
private readonly string value;
public string Value
{
get { return value; }
}
public DisplayStringAttribute(string val)
{
value = val;
}
}
Then I could define my enum like this:
public enum MyState
{
[DisplayString("Ready")]
Ready,
[DisplayString("Not Ready")]
NotReady,
[DisplayString("Error")]
Error
};
And, for my purposes, I created a converter so I could bind to the enum:
public class EnumDisplayNameConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Type t = value.GetType();
if (t.IsEnum)
{
FieldInfo fi = t.GetField(value.ToString());
DisplayStringAttribute[] attrbs = (DisplayStringAttribute[])fi.GetCustomAttributes(typeof(DisplayStringAttribute),
false);
return ((attrbs.Length > 0) && (!String.IsNullOrEmpty(attrbs[0].Value))) ? attrbs[0].Value : value.ToString();
}
else
{
throw new NotImplementedException("Converter is for enum types only");
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Oded's solution probably performs faster, but this solution has a lot of flexibility. You could add a whole bunch of attributes if you really wanted to. However, if you did that, you'd probably be better off creating a class rather than using an enum!
The default value of PackageUnitOfMeasurement.LBS is 0 likewise PackageUnitOfMeasurement.KBS is 1.
So a collection of PackageUnitOfMeasurement is really a collection that contain integer values ( i.e. int ).
Your question is not entirely clear....
A Dictionary collection has a Key and a Value it sounds like you want use PackageUnitOfMeasurement has a Key which is trivial as casting the value to an integer.
PackageUnitOfMeasurement example = PackageUnitOfMeasurement.LBS
int result = (int)example;
var myDict = new Dictionary<PackageUnitOfMeasurement,string>();
myDict.Add(result,"some value here"
Related
I have a dictionary of <string, IComparable> which means the value could Pretty much contain any value type.
Now when I update the value I want to convert a string input to the original type. I have tried all manner of converts but I don't seem to be having any success.
Example
Dictionary val = new Dictionary ();
Val.Add ("test", 1);
Val ["test"]="44"; //this is where I tried convert. But it's not always an int.
So if pre update the value was an int then it will be stored as an int. If it is a byte then it will be stored as an byte.
Can anyone point me in the right direction
Thanks
string id = "type";
val.Add(id, 1);
var type = val[id].getType();
val[id] = System.Convert.ChangeType("44", type);
Assuming that you know all the data types that will be used (int, byte etc), you could use the typeof operator to check for the type before, and then cast to that type from your data.
if (typeof(Val["test]) == int)
{
Val["test"] = int.parse("44");
}
I want to persist the currently selected value of a combo box and restore it at a later time. To manage the value in the combo box I have an enumerated type with description attributes. The description attribute becomes (one of) the combo box string values at runtime and the enumerated constant associated with it is used internally for programming purposes. I got this technique from the following Stack Overflow post:
c#:How to use enum for storing string constants?
That post contained a link in one of the comments to this blog post:
http://weblogs.asp.net/grantbarrington/archive/2009/01/19/enumhelper-getting-a-friendly-description-from-an-enum.aspx
The GetDescription() method that does the enum to string conversion magic is replicated here from that post, with the addition of the "this" keyword to the parameter list so I can use it as an extension method with enumerated types:
public static string GetDescription(this Enum en)
{
Type type = en.GetType();
MemberInfo[] memInfo = type.GetMember(en.ToString());
if (memInfo != null && memInfo.Length > 0)
{
object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attrs != null && attrs.Length > 0)
{
return ((DescriptionAttribute)attrs[0]).Description;
}
}
// Unable to find a description attribute for the enum. Just return the
// value of the ToString() method.
return en.ToString();
}
So I have one side of the equation fully fleshed out and it works quite well. Now I want to go the other way. I want to create a method that takes a string and returns the correct enumerated value by walking the description attributes for a particular enumerated type, and returning the enumerated value associated with the description attribute that matches the string. A hypothetical method declaration would be:
public static Enum GetEnumValue(string str){}
However an immediate problem with that declaration is that it does not return a specific enum type. I am not sure how to declare and cast things properly so that the correct enumerated type is returned. Is it possible to create this complementary method to the GetDescription() method and if so, how do I craft it so it works conveniently for any particular enum type? If I can do this I'll have a convenient solution for the common problem of converting between the strings used to persist control settings and then restoring them later, all supported by enums.
You are missing a piece of information, what Enum to look at.
Currently you are only passing in a string, but not the type of Enum.
The simplest way is to use a generic function
Note that contents of this are off the cuff and probably don't even compile.
public static TEnum GetEnumValue<TEnum>(string str)
where TEnum : struct //enum is not valid here, unfortunately
{
foreach (MemberInfo memInfo in typeof(TEnum).GetMembers())
{
object[] attrs = memInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attrs != null && attrs.Length > 0)
{
if (((DescriptionAttribute)attrs[0]).Description == str)
{
return (TEnum)(object)Enum.Parse(typeof(TEnum),memInfo.Name);
}
}
}
// Unable to find a description attribute for the enum.
return (TEnum)(object)Enum.Parse(typeof(TEnum),str);
}
Then you can use typeof(TEnum) to get the type object for the requested enumeration and do your logic.
Finally you can cast back to TEnum before returning, saving yourself the work on the calling side.
EDIT:
Added a rough example, untested.
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.
How do I get the property name of an object array to use in a SWITCH / CASE function as follows? Any other suggestions on how to do this are appreciated.
Object[] objEURUSD = ConvertStringtoObjectArray(string val1);
Object[] objAUDUSD = ConvertStringtoObjectArray(string val2);
Object[] objGBPUSD = ConvertStringtoObjectArray(string val3);
Example Function Use
Object[] mvavgEURUSD = mvavgObject(objEURUSD);
private Object[] mvavgObject(Object[] val)
{
string sym = val.ToString() // this does not return the name it returns 'System.Object'
switch (sym)
{
case "objEURUSD":
// do something
break;
case "objAUDUSD":
// do something
break;
case "objGBPUSD":
// do something
break;
}
}
I could include the name of the object 'EURUSD' in the Object Array itself, but it's already inlcuded in the name of the object.. I just can't figure out how to reference the name of the object.. either I don't know or I am not conversant with Reflection.
I appreciate your help or suggestions on the matter.
How do I get the property name of an object array to use in a SWITCH / CASE function ?
By not doing the conversion. You can use a string in a switch statement, but not an object[].
You can't because there is no such "name" for an object. That wouldn't even make sense - many variables can hold a reference to the same object. Which one should be the "name"?
Imagine for a moment that there would be such a function called GetName(). What would the program output in each of these cases?
Case 1:
var a = new MyObject();
var b = a;
Console.WriteLine(GetName(b)); // Is it "a" or "b"?
Case 2:
Console.WriteLine(GetName(new MyObject())); // What is the name now?
Case 3:
Console.WriteLine(GetName(null)); // I guess you can return null here, but see case 4:
Case 4:
MyObject a = null;
Console.WriteLine(GetName(a)); // Should this be "a" or null?
And so on and so forth.
If you want to give your objects "names", you'll have to do so yourself. Perhaps create a "NamedObject" wrapper class like this:
class NamedObject<T>
{
public readonly T Object;
public readonly string Name;
public NamedObject(string name, string value)
{
this.Object = value;
this.Name = name;
}
}
Without further information, I can only guess what ConvertStringtoObjectArray does. My guess is that it looks something up using the string as a key, and then returns an array of some values.
In most cases, I would expect that the original string is no longer contained in the object array. Therefore, obviously, you cannot retrieve it, so you will have to “remember” it, i.e. pass it around.
For example, instead of
Object[] objEURUSD = ConvertStringtoObjectArray(myVal);
Object[] mvavgEURUSD = mvavgObject(objEURUSD);
private Object[] mvavgObject(Object[] val)
{
// ...
you could write something like this:
Object[] mvavgEURUSD = mvavgObject(myVal);
private Object[] mvavgObject(string val)
{
Object[] objEURUSD = ConvertStringtoObjectArray(val);
// The string is in the parameter ‘val’, so we can use it here
switch (val)
{
// ...
}
}
the default behavior of ToString() is to return the class name. This can be overridden in any class as all classes inherit from object. string.ToString() is the behavior you are looking for so you probably need to first cast the object back to string (the array part is tricky though since I don't know what the ConvertStringtoObjectArray method does), but you need to do a cast to access what you are looking for.
I assume that the object[] is a char[]? if so the cast should be as easy as casting the object to a char[] and then to the string.
if the variable declaration of "objEURUSD" is in your current scope, you could compare your parameter to that variable to see if both are referencing the same object ... this won't lookup some sort of name, but maybe it's what you want...
Object[] objEURUSD; //initialized somewhere else
private Object[] mvavgObject(Object[] val)
{
if(val==objEURUSD)
{
//it's the array referenced by objEURUSD ...
}
}
as an alternative, you could use a Dictionary<string,Object[]> or Dictionary<Object[],string> to store your arrays, so you can lookup the array for a given name or vice versa
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.