Ordering of elements in C# enumeration with the same integer value - c#

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.

Related

Is a value an enum and -1?

For view model validation, I need to determine whether a value, of which I only have an object interface, is an enum and has the numeric value of -1.
I tried this:
// object value;
if (value?.GetType().IsEnum == true && (int)value == -1) return null;
It should work with my Model enums which are mostly based on int.
But it fails when the value is a Visibility enum (that happens to be also in the view model class and should be ignored for validation) which is based on byte instead of int and that seems not to be castable to int. I could do some more testing but it shouldn't get too slow.
Is there a good simple solution for that? Maybe some test method in the Enum class or something?
You can check the underlying type with GetEnumUnderlyingType():
Type t = value?.GetType();
if (t?.IsEnum == true &&
t?.GetEnumUnderlyingType() == typeof(int) &&
(int)value == -1)
return null;
Since a byte can never be -1, you don't need to check it. But you may need to extend the check for long enums, too.
UPDATE:
I just tried around a little and found that Convert.ToInt32() also solves your problem:
if (value?.GetType().IsEnum == true &&
Convert.ToInt64(value) == -1)
return null;
This seems cleaner and also works for all possible underlying types.
Another update: Unfortunatly the solution above is not as clean as I thought. Even with Convert.ToInt64() solves the problem of long values too big for Int32, but it throws if you pass for example a ulong.MaxValue.
So you have to choose a type that is large enough for all possible enum base types:
if (value?.GetType().IsEnum == true &&
Convert.ToDecimal(value) == -1)
return null;
Using Convert.ToDecimal() this passes all the test cases that came up so far.
In addition, one could consider using the Enum.IsDefined Method (Type, Object) to verify, if the value is a valid enum.
That does of course not cover the "check if it is -1" part.
Enum.IsDefined Method (Type, Object)
Returns an indication whether a constant with a specified value exists
in a specified enumeration. Namespace: System Assembly: mscorlib
(in mscorlib.dll)
[ComVisibleAttribute(true)] public static bool IsDefined( Type enumType, object value )
Parameters
enumTypeType: System.Type
An enumeration type.
valueType: System.Object The value or name of a constant in enumType.
Return Value
Type: System.Boolean becomes true if a constant in enumType has a
value equal to value; otherwise, false.
Exceptions
ArgumentNullException' enumType or value is null.
ArgumentException` enumType is not an Enum. -or- The type of value is an enumeration, but it is not an enumeration of type enumType. -or- The type of value is not an underlying type of enumType.
InvalidOperationException value is not type SByte, Int16, Int32, Int64, Byte, UInt16, UInt32, UInt64, or String.
Remarks
The value parameter can be any of the following:
Any member of type enumType.
A variable whose value is an enumeration member of type enumType.
The string representation of the name of an enumeration member. The
characters in the string must have the same case as the enumeration
member name.
A value of the underlying type of enumType.
If the constants in enumType define a set of bit fields and value
contains the values, names, or underlying values of multiple bit
fields, the IsDefined method returns false. In other words, for
enumerations that define a set of bit fields, the method determines
only whether a single bit field belongs to the enumeration. To
determine whether multiple bit fields are set in an enumeration type
that is tagged with the FlagsAttribute attribute, you can call the
HasFlag method.
You could:
if (value != null)
{
var type = value.GetType();
if (type.IsEnum && object.Equals(Enum.ToObject(type, -1), value))
{
return null;
}
}
I had to do it in multiple lines to "cache" the GetType(). The trick is using Enum.ToObject() to "cast" -1 to the current Enum type.
Note that casting will generate "strange" results:
public enum TestEnum : ulong
{
ValueMax = ulong.MaxValue
}
In this case, TestEnum.ValueMax will return null. This will happen for all the unsigned types.
byte can be casted to int if it is not boxed with object
byte b = 1;
int i = (int)b; //works good
int f = (int)(object)b; //fails
So you could convert your variable to int using Convert.ToInt32 method, as René Vogt suggested, or cast it to dynamic instead casting to int:
if (value?.GetType().IsEnum == true && (dynamic)value == -1) return null;
Although, operations with dynamic are rather slow. In my opinion, soultion with Convert.ToInt32 is the most cleanable and efficient. This answer is just to point out, why you can not cast the object to int and suggest a dynamic version of cast.

Converting strings to enumerated types in C#?

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.

How can I tell the compiler that my generics will definitely allow "|=" or casting to int?

I'm bone new to generics in C#, and I'm attempting to create a method that will use generics. I tripped across the where keyword when attempting to create a local variable, so I'm sure that it will be part of the solution.
The reason for this is I have several different enum variables but the method would do the same to each (given a string that is one of the enum's defined values, toggle it on, using the enum like a bit field).
I have most of this together, the part I'm now stuck at is being able to tell the generic method it's ok to allow "|=" as I'm certain any type passed in will support the operator.
I would like to be able to maintain generality, if possible, so could either be an enum or a List and I would execute different code paths depending on the type.
Example of what I'm taking about
enum someType { value1 = 1<<0, value2 = 1<<1, value3 = 1<<2 }; // and so on
// some more enums
private T someMethod<T>(string myIdentifyers)
where T: new()
{
// suppose myIdentifiers is 1 more more of the valid enum options
// assume that the correct strings and enum combinations will be passed
T retval = new T();
while () {
// assume a loop with a method call that parses off one identifyer at a time
// if I have to convert the enum to an int first, so be it, but
// at this point I can't tell it that a cast to int exists
retval |= (T)System.Enum.Parse( typeof(T), oneIdentifyer, false );
}
return retval;
}
I would try something like this (pseudocode):
[Flags]
enum someType { value1 = 1<<0, value2 = 1<<1, value3 = 1<<2 }; // and so on
// some more enums
private T someMethod<T>(string myIdentifyers)
where T: struct, new()
{
// check if enum
if (typeof(T).BaseType != typeof(Enum)) // we should probably check for the flags attribute too
throw new Exception();
// suppose myIdentifiers is 1 more more of the valid enum options
// assume that the correct strings and enum combinations will be passed
int retval = 0;
while () {
// assume a loop with a method call that parses off one identifyer at a time
// if I have to convert the enum to an int first, so be it, but
// at this point I can't tell it that a cast to int exists
retval |= (int) (object) System.Enum.Parse( typeof(T), oneIdentifyer, false );
}
return (T) (object) retval;
}
It's not possible to express enum generic-constraints as of C# 4. Nor is it possible to express operator-constraints.
That said, I think you're looking for a method like this:
public static T AssembleFlagsEnum<T>(IEnumerable<string> names) where T : struct
{
return (T)(object)names.Aggregate(0,
(valSoFar, name) => valSoFar | Convert.ToInt32(Enum.Parse(typeof(T), name)));
}
Note that this doesn't validate that the type is a [Flags] enum. Nor will it work for enums which have underlying-types other than int.
In general, there's no good way to invoke operators on a generic type, although there are some kludges and workarounds that can help out in some circumstances.
See this similar question: Generic C# Code and the Plus Operator
In this case, since you know your enums cast to and from int, I think Bruno's method is the way to go.
Unfortunately it's not possible.
You can restrict with the struct constraint to say value type but obviously that includes more than integers. The only thing you could then do is early in the code to check on the actual type.
Supported constraint restrictions are described at http://msdn.microsoft.com/en-us/library/d5x73970.aspx
You can't do operator constraints - see Solution for overloaded operator constraint in .NET generics

Dictionary of Enum Values as strings

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"

How to create class containing property of various types in C#

I'm trying to create a class to represent the value of a column in SQL. Here's the final goal I want to reach:
public string GenerateInsertSql()
{
StringBuilder insertSql = new StringBuilder();
insertSql.Append("INSERT INTO " + SchemaName + "." + TableName + "\n");
insertSql.Append("(\n");
int counter = 0;
foreach (ColumnValue columnValue in _insertValues)
{
if (counter > 0) insertSql.Append(",");
counter++;
insertSql.Append(columnValue.ColumnName).Append("\n");
}
insertSql.Append(")\n");
insertSql.Append("VALUES\n");
insertSql.Append("(\n");
counter = 0;
foreach (ColumnValue columnValue in _insertValues)
{
if (counter > 0) insertSql.Append(",");
counter++;
insertSql.Append(columnValue.SqlValue).Append("\n");
}
insertSql.Append(")\n");
}
So the class is the ColumnValue class mentioned above. The ColumnValue class needs a Value property which can presumably be any basic .NET type (int, string etc). The class also has a property, SqlValue, which provides a string representation of the Value property which I can use in the generated SQL statement. So if Value is an int, then SqlValue just gives Value.ToString(), whereas if Value is a string, SqlValue needs to check if the string is null in order to either return 'NULL' or the Value property itself.
Initially I tried to have Value as an object and SqlValue to test the type of Value in a switch statement:
public string SqlValue
{
get
{
switch (Value.GetType())
{
...
}
}
}
But then I get 'Value of integral type expected'. So I can't check an object's type.
Next I tried generics. I changed the ColumnValue class to ColumnValue<T>, with the Value property as follows:
public T Value { get; set; }
And then, in the SqlValue property, another check of the type of T, but I'm not allowed to do that either.
Is there a way to achieve this in C# without simply creating separate classes to handle each possible type of column?
switch keyword can take only integers (correctly speaking, integral type (see here)). But you can always use "if" statements. For example,
var t = value.GetType();
if (t == typeof(string))
{
..
}
else if (t == typeof(int))
{
}
else if ...
You can also use "is" keyword. For example,
if (value is string)
{
...
}
else if (value is int)
{
...
}
else if ...
"is" operator will also check within inheritance hierarchy but I don't think that will be the case in your usage.
Lastly, I hope that whatever SqlValue implementation that you are going to do will handle escaping (for example - quotes within string values) and will protect against sql injection attacks. You may consider generating parameterized query insetad (i.e. add parameter for each column in the query and then pass actual values as parameter values - so you won't need SqlValue property at all).
If you use parameters, you can have your SqlValue just return an object.
Your example will need to add quotes around strings and deal with culture issues (formatting of double, date).
You can also use Type.GetTypeCode(Value.GetType()), which will turn it into the enum TypeCode which has all the primitive values retrieval from a database, especially enumerated. With the exception of Guid which will come up as object, but you can handle that in the object block.

Categories

Resources