Enum from string, int, etc - c#

Using extension method we can create methods to convert an enum to other datatype like string, int by creating extension methods ToInt(), ToString(), etc for the enum.
I wonder how to implement the other way around, e.g. FromInt(int), FromString(string), etc. As far as I know I can't create MyEnum.FromInt() (static) extension method. So what are the possible approaches for this?

I would avoid polluting int or string with extension methods for enums, instead a good old fashioned static helper class might be in order.
public static class EnumHelper
{
public static T FromInt<T>(int value)
{
return (T)value;
}
public static T FromString<T>(string value)
{
return (T) Enum.Parse(typeof(T),value);
}
}

Do you really need those extension methods?
MyEnum fromInt = (MyEnum)someIntValue;
MyEnum fromString = (MyEnum)Enum.Parse(typeof(MyEnum), someStringValue, true);
int intFromEnum = (int)MyEnum.SomeValue;
string stringFromEnum = MyEnum.SomeValue.ToString();

The other way around would be possibly... the other way around ;) Extend int and string with generic extension methods which will take as type parameter the type of an enum:
public static TEnum ToEnum<TEnum>(this int val)
{
return (TEnum) System.Enum.ToObject(typeof(TEnum), val);
}
public static TEnum ToEnum<TEnum>(this string val)
{
return (TEnum) System.Enum.Parse(typeof(TEnum), val);
}
Usage:
var redFromInt = 141.ToEnum<System.Drawing.KnownColor>();
var redFromString = "Red".ToEnum<System.Drawing.KnownColor>();
There is unfortunately no generic constraint for Enums, so we have to check TEnum type during runtime; to simplify we'll leave that verification to Enum.ToObject and Enum.Parse methods.

why do you want FromInt an extenstion method versus just casting it?
MyEnum fromInt;
if(Enum.IsDefined(typeof(MyEnum), intvalue))
{
fromInt = (MyEnum) intvalue;
}
else
{
//not valid
}
alternatively, for strings, you can use Enum.TryParse
MyEnum fromString;
if (Enum.TryParse<MyEnum>(stringvalue, out fromString))
{
//succeeded
}
else
{
//not valid
}

Another approach (for the string part of your question):
/// <summary>
/// Static class for generic parsing of string to enum
/// </summary>
/// <typeparam name="T">Type of the enum to be parsed to</typeparam>
public static class Enum<T>
{
/// <summary>
/// Parses the specified value from string to the given Enum type.
/// </summary>
/// <param name="value">The value.</param>
/// <returns></returns>
public static T Parse(string value)
{
//Null check
if(value == null) throw new ArgumentNullException("value");
//Empty string check
value = value.Trim();
if(value.Length == 0) throw new ArgumentException("Must specify valid information for parsing in the string", "value");
//Not enum check
Type t = typeof(T);
if(!t.IsEnum) throw new ArgumentException("Type provided must be an Enum", "T");
return (T)Enum.Parse(typeof(T), value);
}
}
(Partially inspired by: http://devlicious.com/blogs/christopher_bennage/archive/2007/09/13/my-new-little-friend-enum-lt-t-gt.aspx)

You can do:
public static class EnumExtensions
{
public static Enum FromInt32(this Enum obj, Int32 value)
{
return (Enum)((Object)(value));
}
public static Enum FromString(this Enum obj, String value)
{
return (Enum)Enum.Parse(obj.GetType(), value);
}
}
Or:
public static class Int32Extensions
{
public static Enum ToEnum(this Int32 obj)
{
return (Enum)((Object)(obj));
}
}
public static class StringExtensions
{
public static Enum ToEnum(this Enum obj, String value)
{
return (Enum)Enum.Parse(obj.GetType(), value);
}
}

You can either make extension methods on int and string.
Or make static method on some other static class. Maybe something like EnumHelper.FromInt(int).
But I would pose one question : Why do you want to convert to string or int? Its not how you normaly work with enumerables, except maybe serialisation. But that should be handled by some kind of infrastructure, not your own code.

Related

Return instance of deriving class from abstract generic method

What I want to do here is a bit hard to describe. My current needs require that I have an enum type that can implement an interface. While not the prettiest solution, this is what I came up with;
public class EnumClass<T> where T : Enum
{
public T Value { get; }
public string Name { get; }
public EnumClass(T enumValue)
{
Value = enumValue;
Name = Enum.GetName(typeof(T), enumValue);
}
public static EnumClass<T> Parse(string name)
{
return new EnumClass<T>((T)Enum.Parse(typeof(T), name));
}
}
Here is an example implementation:
public class AnimalTypes : EnumClass<AnimalTypesEnum>, IMyEnumInterface
{
public AnimalTypes (AnimalTypesEnum value) : base(value) { }
}
public enum AnimalTypesEnum
{
[Description("Cat")]
CAT,
[Description("Dog")]
DOG,
[Description("Horse")]
HORSE,
[Description("Bear")]
BEAR
}
When I call Parse statically on an inheritor, I have to manually cast the result back to the inheritor type from the base type, since Parse returns a generic EnumClass<T> object.
ex.
AnimalTypes dog = (AnimalTypes)AnimalTypes.Parse("DOG");
My question essentially is, is there any way to write Parse such that it returns the type of the inheritor, and not the base class? I'd also like to be able to mark EnumClass<T> abstract, but if I try doing so now, the compiler will not compile Parse, stating that I cannot create an abstract instance of type EnumClass<T> with which to return.
You can use a curiously recursive template pattern, but it requires default constructors and feels odd. Normally if things get this convoluted it's worth asking if your requirements can be restructured so that it's not so complicated, but it's hard to know if that's possible with the details given. That said, this may be as close to what you are asking for that you can get.
There isn't a way to specify that a method return the derived type, but you can specify the return type using a generic type. Below is the EnumClass, but modified to take two generic types. The first type is the enum type like before, but the second is for specifying the derived type (hence the recursive part of the template).
public abstract class EnumClass<T, TDerived>
where T : Enum where TDerived : EnumClass<T, TDerived>, new()
{
protected EnumClass()
{
}
protected EnumClass(T enumValue)
{
Value = enumValue;
}
private T _value = default(T);
public T Value
{
get => _value;
init => _value = value;
}
private string _name = null;
public string Name
{
get
{
_name = _name ?? Enum.GetName(typeof(T), Value);
return _name;
}
}
public static TDerived Parse(string name)
{
var enumValue = (T)Enum.Parse(typeof(T), name);
return new TDerived() {Value = enumValue};
}
}
Then, a derived type using this EnumClass would look like this, where the second generic type recursively refers to itself, which means that the static Parse method in the EnumClass will return a type AnimalTypes.
public class AnimalTypes : EnumClass<AnimalTypesEnum, AnimalTypes>
{
public AnimalTypes(): base()
{
}
public AnimalTypes(AnimalTypesEnum value): base(value)
{
}
}
In use, it would look like this
//because we are required to have public default constructors, it's possible
//to have a "default" AnimalTypes class that would be similar to constructing
//a "new AnimalTypes(default(AnimalTypesEnum));"
var defaultType = new AnimalTypes();
//this will output "CAT, CAT"
Console.WriteLine($"{defaultType.Value}, {defaultType.Name}");
//Since we are using init, you can initialize the value using this format
//instead of using the constructor
var horseType = new AnimalTypes() {Value = AnimalTypesEnum.HORSE};
//this will output "HORSE, HORSE"
Console.WriteLine($"{horseType.Value}, {horseType.Name}");
//normal constructor
var dogType = new AnimalTypes(AnimalTypesEnum.DOG);
//this will output "DOG, DOG"
Console.WriteLine($"{dogType.Value}, {dogType.Name}");
//static parser will return a type of AnimalTypes
var bearType = AnimalTypes.Parse("BEAR");
//this will output "BEAR, BEAR"
Console.WriteLine($"{bearType.Value}, {bearType.Name}");
You need to add another type param, in order to parametrize the return value type of Parse and enable derived/inherited types being created.
Usage:
var bear = EnumClass<AnimalTypesEnum>.Parse<AnimalTypes>("BEAR");
//AnimalTypesEnum unchanged
//AnimalTypes unchanged
public abstract class EnumClass<TEnum> where TEnum : Enum
{
public TEnum Value { get; }
public string Name { get; }
protected EnumClass(TEnum enumValue)
{
Value = enumValue;
Name = Enum.GetName(typeof(TEnum), enumValue);
}
public static TEnumClass Parse<TEnumClass>(string name)
where TEnumClass : EnumClass<TEnum>
{
//TODO: try/catch
/* Contract: the derived class must have a public constructor
that takes 1 arg of its enum type.
Generic constraints don't support ctors with args, so we need reflection here... */
return (TEnumClass)Activator.CreateInstance(
typeof(TEnumClass), Enum.Parse(typeof(TEnum), name));
}
}

Return Enum Name from Integer in LINQ Database Query

I would like to return the string value of an enum stored as an integer in a database using a LINQ query.
What I have tried:
return (from a in context.Tasks
select new TaskSearch
{
TaskID = a.TaskID,
TaskTypeName = Enum.GetName(typeof(TaskTypeEnum), a.TaskType)
}).ToList();
I'm using asp.net mvc.
Exception:
An exception of type 'System.NotSupportedException' occurred in EntityFramework.SqlServer.dll but was not handled in user code
Additional information: LINQ to Entities does not recognize the method 'System.String GetName(System.Type, System.Object)' method, and this method cannot be translated into a store expression.
You need to materialize your query (a query must be able to be converted to a sql statement, but Enum.GetName() cannot be converted to sql)
Try
((from a in context.Tasks select a).AsEnumerable().Select(t => new TaskSearch
{
TaskID = t.TaskID,
TaskTypeName = Enum.GetName(typeof(TaskTypeEnum), t.TaskType)
}).ToList());
I define a set of Enum extensions like this which I have found useful. Unfortunately the Type constraints cannot be refined beyond struct, so you must externally ensure that the methods are only called on Enums.:
/// <summary>Type-safe extension methods for parsing Enums.</summary>
public static partial class EnumExtensions{
#region Enum Parsing utilities
/// <summary>Typesafe wrapper for <c>Enum.GetValues(typeof(TEnum).</c></summary>
public static ReadOnlyCollection<TEnum> EnumGetValues<TEnum>() {
return new ReadOnlyCollection<TEnum>((TEnum[])(Enum.GetValues(typeof(TEnum))));
}
/// <summary>TODO</summary>
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
public static ReadOnlyCollection<string> EnumGetNames<TEnum>() where TEnum : struct {
return new ReadOnlyCollection<string>((string[])(Enum.GetNames(typeof(TEnum))));
}
/// <summary>Typesafe wrapper for <c>Enum.ParseEnum()</c> that automatically checks
/// constants for membership in the <c>enum</c>.</summary>
public static TEnum ParseEnum<TEnum>(string value) where TEnum : struct {
return ParseEnum<TEnum>(value,true);
}
/// <summary>Typesafe wrapper for <c>Enum.ParseEnum()</c> that automatically checks
/// constants for membership in the <c>enum</c>.</summary>
public static TEnum ParseEnum<TEnum>(string value, bool checkConstants) where TEnum : struct {
TEnum enumValue;
if (!TryParseEnum<TEnum>(value, out enumValue) && checkConstants)
throw new ArgumentOutOfRangeException("value",value,"Enum type: " + typeof(TEnum).Name);
return enumValue;
}
/// <summary>Typesafe wrapper for <c>Enum.TryParseEnum()</c> that automatically checks
/// constants for membership in the <c>enum</c>.</summary>
public static bool TryParseEnum<TEnum>(string value, out TEnum enumValue) where TEnum : struct {
return Enum.TryParse<TEnum>(value, out enumValue)
&& Enum.IsDefined(typeof(TEnum),enumValue);
}
/// <summary>Typesafe wrapper for <c>Enum.ToObject()</c>.</summary>
/// <typeparam name="TEnum"></typeparam>
public static TEnum EnumParse<TEnum>(char c, string lookup) {
if (lookup==null) throw new ArgumentNullException("lookup");
var index = lookup.IndexOf(c);
if (index == -1) throw new ArgumentOutOfRangeException("c",c,"Enum Type: " + typeof(TEnum).Name);
return (TEnum) Enum.ToObject(typeof(TEnum), index);
}
#endregion
}

java enums vs C# enums - missing features

in java I could easily describe an enum with aditional data.
I could describe it something like this
public enum OperatorType
{
GreaterOrEqual (">=", "GreaterOrEqual"),
Greater (">" ,"Greater"),
Less ("<", "Less"),
LessOrEqual ("<=", "LessOrEqual"),
Equal ("==", "Equal"),
Between ("Between", "Between"),
Around ("Around","Around");
private final String symbol;
private final String name;
private OperatorType(final String symbol, final String name) {
this.symbol = symbol;
this.name = name;
}
}
And then add a static method that iterates over values(), adds all data to a hashmap and allow to retrieve from the map full enum data by one of its attriburtes as a key.
In brief, enum is a very developed type in java.
Now,
moving to c#, what are my options?
I want to hold an enum with its attributes, load it to a map, and retreive by key when I need. Do I have anything to assist (like, a singletone for each enum - which is not a good idea).
Thanks.
I would just create a class with public static readonly instances of each type and ditch enums altogether. You can use them as dictionary keys or do whatever you like. If you still intend to map them to an underlying data type (int) then you can create implicit operators for that too.
public class OperatorType
{
private static readonly Dictionary<int, OperatorType> OperatorMapping = new Dictionary<int, OperatorType>();
public static readonly OperatorType GreaterOrEqual = new OperatorType(0, ">=", "GreaterOrEqual");
public static readonly OperatorType Greater = new OperatorType(1, ">", "Greater");
public readonly String symbol;
public readonly String name;
private readonly int underlyingValue;
private OperatorType(int underlyingValue, string symbol, string name) {
this.underlyingValue = underlyingValue;
OperatorMapping[underlyingValue] = this;
this.symbol = symbol;
this.name = name;
}
public static implicit operator int(OperatorType operatorType)
{
return operatorType.underlyingValue;
}
public static implicit operator OperatorType(int value)
{
return OperatorMapping[value];
}
}
Sample usage:
Dictionary<OperatorType, string> operators = new Dictionary<OperatorType, string>();
operators.Add(OperatorType.GreaterOrEqual, "Greater or equal");
Console.WriteLine(operators[OperatorType.GreaterOrEqual]); //"Greater or equal"
OperatorType operatorType = 1;
Console.WriteLine(operatorType.name); //"Greater"
If you don't care about an underlying value, don't include it. Also consider whether or not the Dictionary mapping should be threadsafe for your usage. You can also expose a static IEnumerable<OperatorType> (or other collection) to get all operators defined if you want.
EDIT: On second thought, explicit operators are possibly preferable instead of implicit, both to conform with typical .NET best practices and to better match typical enum conversions.
The most convinient workaround might be to create an extension method to your enum type, and return the associated symbols.
Something like this:
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
tester t = tester.x;
t.testenums();
Console.ReadKey();
}
}
public static class ext
{
public static void testenums(this tester x)
{
Console.WriteLine(x.ToString());
}
}
public enum tester
{
x,
y
}
}
Of course you can write a more complex extension method, with return value, etc, this is just an example how to do it.
You can create an attribute:
public class EnumKeyAttribute : Attribute
{
public string Key { get; set; }
public string Description { get; set; }
public EnumKeyAttribute(string key, string description)
{
this.Key = key;
this.Description = description;
}
}
Then apply it to your enum
public enum OperatorType
{
[EnumKey(">=", "GreaterOrEqual")]
GreaterOrEqual,
[EnumKey(">", "Greater")]
Greater,
[EnumKey("<", "Less")]
Less,
[EnumKey("<=", "LessOrEqual")]
LessOrEqual,
[EnumKey("==", "Equal")]
Equal,
[EnumKey("Between", "Between")]
Between,
[EnumKey("Around", "Around")]
Around
}
To get the attribute data you can use reflection. Below is an example of getting the attribute for "Less"
MemberInfo memberInfo = typeof(OperatorType).GetMember(OperatorType.Less.ToString()).FirstOrDefault();
if(memberInfo != null)
{
EnumKeyAttribute attribute = (EnumKeyAttribute)memberInfo.GetCustomAttributes(typeof(EnumKeyAttribute), false).FirstOrDefault();
Console.WriteLine(attribute.Key);
Console.WriteLine(attribute.Description);
}
But because these enums are not created at runtime you can increase your efficiency by creating a static method that looks up the value in a dictionary. Do this as an extension method for ease of use
public static class KeyFinder
{
private static Dictionary<OperatorType, EnumKeyAttribute> lookupTable =
new Dictionary<OperatorType, EnumKeyAttribute>();
public static EnumKeyAttribute GetKey(this OperatorType type)
{
if (lookupTable.ContainsKey(type))
{
return lookupTable[type];
}
MemberInfo memberInfo = typeof(OperatorType).GetMember(type.ToString()).FirstOrDefault();
if (memberInfo != null)
{
EnumKeyAttribute attribute = (EnumKeyAttribute)memberInfo.GetCustomAttributes(typeof(EnumKeyAttribute), false).FirstOrDefault();
if (attribute != null)
{
lookupTable.Add(type, attribute);
return attribute;
}
}
// add a null value so next time it doesn't use reflection only to find nothing
lookupTable.Add(type, null);
return null;
}
}
So now to get the values you simply do the following:
OperatorType.Less.GetKey().Key
OperatorType.Less.GetKey().Description
Just be careful of null reference exceptions (since it will return null if it can't find an attribute). If you want to find by key you can simply create other extension methods that use the string value as the key.
C# doesn't really have the same feature. However there are several possibilities to get really close (and potentially more flexible as well).
Sticking to regular enums, you could use attributes to enrich with extra information. Of course, this requires reflection to work with that
public enum OperatorType
{
[DisplayName(">=")]
GreaterOrEqual,
// ...
}
There are several patterns to work with this, e.g. http://www.codeproject.com/Articles/28087/DisplayNameAttribute-for-Enumerations, google for more.
Another approach can be to enhance your enumeration types using regular classes:
public class OperatorType
{
public static OperatorType GreaterOrEqual = new OperatorType(">=", "GreaterOrEqual");
// ...
string symbol;
string name;
private OperatorType(string symbol, string name)
{
this.symbol = symbol;
this.name = name;
}
}
This article describes some other ways to work with enum-like types in C#
If you really need the functionality of Java-style enums in C#, I see three reasonable ways to implement it:
Use a C# enum and a static class of helper methods. You lose type safety, but this is an otherwise very workable solution.
Use a C# enum and a set of extension methods. Probably the most idiomatic C# solution, but you still have to deal with the loss of type safety (your extension methods should be able to cope with out-of-range values, even if only by throwing an exception).
Use the type-safe enum pattern that was common in Java before the language gained the enum keyword in Java 5. If you have non-trivial logic for each enum value, this would be my preference.

Converting string back to enum

Is there a cleaner, more clever way to do this?
I'm hitting a DB to get data to fill an object and am converting a database string value back into its enum (we can assume that all values in the database are indeed values in the matching enum)
The line in question is the line below that sets EventLog.ActionType...the reason I began to question my method is because after the equals sign, VS2010 keeps trying to override what I'm typing by putting this: "= EventActionType("
using (..<snip>..)
{
while (reader.Read())
{
// <snip>
eventLog.ActionType = (EventActionType)Enum.Parse(typeof(EventActionType), reader[3].ToString());
...etc...
As far as I know, this is the best way to do it. I've set up a utility class to wrap this functionality with methods that make this look cleaner, though.
/// <summary>
/// Convenience method to parse a string as an enum type
/// </summary>
public static T ParseEnum<T>(this string enumValue)
where T : struct, IConvertible
{
return EnumUtil<T>.Parse(enumValue);
}
/// <summary>
/// Utility methods for enum values. This static type will fail to initialize
/// (throwing a <see cref="TypeInitializationException"/>) if
/// you try to provide a value that is not an enum.
/// </summary>
/// <typeparam name="T">An enum type. </typeparam>
public static class EnumUtil<T>
where T : struct, IConvertible // Try to get as much of a static check as we can.
{
// The .NET framework doesn't provide a compile-checked
// way to ensure that a type is an enum, so we have to check when the type
// is statically invoked.
static EnumUtil()
{
// Throw Exception on static initialization if the given type isn't an enum.
Require.That(typeof (T).IsEnum, () => typeof(T).FullName + " is not an enum type.");
}
public static T Parse(string enumValue)
{
var parsedValue = (T)System.Enum.Parse(typeof (T), enumValue);
//Require that the parsed value is defined
Require.That(parsedValue.IsDefined(),
() => new ArgumentException(string.Format("{0} is not a defined value for enum type {1}",
enumValue, typeof(T).FullName)));
return parsedValue;
}
public static bool IsDefined(T enumValue)
{
return System.Enum.IsDefined(typeof (T), enumValue);
}
}
With these utility methods, you can just say:
eventLog.ActionType = reader[3].ToString().ParseEnum<EventActionType>();
You can use extension methods to give some syntactic sugar to your code. You can even made this extension methods generics.
This is the kind of code I'm talking about: http://geekswithblogs.net/sdorman/archive/2007/09/25/Generic-Enum-Parsing-with-Extension-Methods.aspx
public static T EnumParse<T>(this string value)
{
return EnumHelper.EnumParse<T>(value, false);
}
public static T EnumParse<T>(this string value, bool ignoreCase)
{
if (value == null)
{
throw new ArgumentNullException("value");
}
value = value.Trim();
if (value.Length == 0)
{
throw new ArgumentException("Must specify valid information for parsing in the string.", "value");
}
Type t = typeof(T);
if (!t.IsEnum)
{
throw new ArgumentException("Type provided must be an Enum.", "T");
}
T enumType = (T)Enum.Parse(t, value, ignoreCase);
return enumType;
}
SimpleEnum enumVal = Enum.Parse<SimpleEnum>(stringValue);
Could use an extension method like so:
public static EventActionType ToEventActionType(this Blah #this) {
return (EventActionType)Enum.Parse(typeof(EventActionType), #this.ToString());
}
And use it like:
eventLog.ActionType = reader[3].ToEventActionType();
Where Blah above is the type of reader[3].
You could get any enum value back, not just EventActionType as in your case with the followin method.
public static T GetEnumFromName<T>(this object #enum)
{
return (T)Enum.Parse(typeof(T), #enum.ToString());
}
You can call it then,
eventLog.ActionType = reader[3].GetEnumFromName<EventActionType>()
This is a more generic approach.
#StriplingWarrior's answer didn't work at first try, so I made some modifications:
Helpers/EnumParser.cs
namespace MyProject.Helpers
{
/// <summary>
/// Utility methods for enum values. This static type will fail to initialize
/// (throwing a <see cref="TypeInitializationException"/>) if
/// you try to provide a value that is not an enum.
/// </summary>
/// <typeparam name="T">An enum type. </typeparam>
public static class EnumParser<T>
where T : struct, IConvertible // Try to get as much of a static check as we can.
{
// The .NET framework doesn't provide a compile-checked
// way to ensure that a type is an enum, so we have to check when the type
// is statically invoked.
static EnumParser()
{
// Throw Exception on static initialization if the given type isn't an enum.
if (!typeof (T).IsEnum)
throw new Exception(typeof(T).FullName + " is not an enum type.");
}
public static T Parse(string enumValue)
{
var parsedValue = (T)Enum.Parse(typeof (T), enumValue);
//Require that the parsed value is defined
if (!IsDefined(parsedValue))
throw new ArgumentException(string.Format("{0} is not a defined value for enum type {1}",
enumValue, typeof(T).FullName));
return parsedValue;
}
public static bool IsDefined(T enumValue)
{
return Enum.IsDefined(typeof (T), enumValue);
}
}
}
Extensions/ParseEnumExtension.cs
namespace MyProject.Extensions
{
public static class ParseEnumExtension
{
/// <summary>
/// Convenience method to parse a string as an enum type
/// </summary>
public static T ParseEnum<T>(this string enumValue)
where T : struct, IConvertible
{
return EnumParser<T>.Parse(enumValue);
}
}
}

Behaviour to simulate an enum implementing an interface

Say I have an enum something like:
enum OrderStatus
{
AwaitingAuthorization,
InProduction,
AwaitingDespatch
}
I've also created an extension method on my enum to tidy up the displayed values in the UI, so I have something like:
public static string ToDisplayString(this OrderStatus status)
{
switch (status)
{
case Status.AwaitingAuthorization:
return "Awaiting Authorization";
case Status.InProduction:
return "Item in Production";
... etc
}
}
Inspired by the excellent post here, I want to bind my enums to a SelectList with an extension method:
public static SelectList ToSelectList<TEnum>(this TEnum enumObj)
however, to use the DisplayString values in the UI drop down I'd need to add a constraint along the lines of
: where TEnum has extension ToDisplayString
Obviously none of this is going to work at all with the current approach, unless there's some clever trick I don't know about.
Does anyone have any ideas about how I might be able to implement something like this?
Is there a compelling reason to use an enum here?
When you start jumping through crazy hoops to use enums, it might be time to use a class.
public class OrderStatus
{
OrderStatus(string display) { this.display = display; }
string display;
public override string ToString(){ return display; }
public static readonly OrderStatus AwaitingAuthorization
= new OrderStatus("Awaiting Authorization");
public static readonly OrderStatus InProduction
= new OrderStatus("Item in Production");
public static readonly OrderStatus AwaitingDispatch
= new OrderStatus("Awaiting Dispatch");
}
You consume it the same as an enum:
public void AuthorizeAndSendToProduction(Order order, ProductionQueue queue)
{
if(order.Status != OrderStatus.AwaitingAuthorization)
{
Console.WriteLine("This order is not awaiting authorization!");
return;
}
order.Status = OrderStatus.InProduction;
queue.Enqueue(order);
}
The string representation is built-in, and all you need is ToString().
Of course, you can use the DisplayAttribute to annotate your Enums.
enum OrderStatus
{
[Display(Description="Long Desc", Name="Awaiting Authorization", ShortName="Wait Auth")]
AwaitingAuthorization,
[Display(Description="...", Name="...", ShortName="...")]
InProduction,
[Display(Description="...", Name="...", ShortName="...")]
AwaitingDespatch
}
You can also opt to create an extension method taking any enumeration value and returning its display name based on the attribute set to it to tidy up the displayed values in the UI, as follows:
public static class EnumExtensions
{
public static string ToName(this Enum enumValue)
{
var displayAttribute = enumValue.GetType()
.GetMember(enumValue.ToString())[0]
.GetCustomAttributes(false)
.Select(a => a as DisplayAttribute)
.FirstOrDefault();
return displayAttribute?.Name ?? enumValue.ToString();
}
}
With
public enum Test
{
[Display(Name="AAA")]
a,
b
}
Code:
Console.WriteLine(Test.a.ToName());
Console.WriteLine(Test.b.ToName());
Results
AAA
b
I want to bind my enums to a SelectList with an extension method:
For type safety, I wouldn't use an extension methods, but instead a static class that deals with the Enum type:
Pre C# 7.3 version. Since Enum is not a valid type constraint prior to 7.3 (and it would cause a compile-time exception), you'll end up by considering that enums are value types and they implement some interfaces, in order to restrict the type parameter as close to Enum as possible.
public static class Enums<TEnum> where TEnum : struct, IComparable, IFormattable, IConvertible
{
static Enums()
{
if (!typeof(TEnum).IsEnum)
{
throw new InvalidOperationException();
}
}
}
C# 7.3+ version, with compile time checking... yay!
public static class Enums<TEnum> where TEnum : Enum
{
}
GetValues Method for the class:
public static IEnumerable<TEnum> GetValues(bool includeFirst)
{
var result = ((TEnum[])Enum.GetValues(typeof(TEnum))).ToList();
if (!includeZero)
result = result.Where(r => r != default).ToList();
return result;
}
If you follow Enum Guidelines and include the Default (zero) value, we can ignore it (sometimes we want to display the value like "None Selected" and sometimes we don't "Invalid Selection").
Then we can add another method:
public static IEnumerable<string> GetNames(bool includeFirst)
{
var result = GetValue(includeFirst)
.Select(v => v.ToName())
.ToList();
return result;
}
Instead of using "ToDisplayString", simply override ToString() of your enum. So if an enum overrides it it will take it, otherwise it will take the default ToString behavior (in ToSelectList).
If you just need to use relatively tiny enumerate classes that have no more than an explicit casting operator, ToString and do not take other usability for the special ones about enum on System and its derived namespaces, then the following example could be a solution:
namespace MyNamespace {
public abstract class EnumerateClass<Type, InheritingClass> : IEquatable<InheritingClass>
where Type : IEquatable<Type>
where InheritingClass : EnumerateClass<Type, InheritingClass> {
internal readonly Type Identifier;
protected EnumerateClass (Type identifier) {
this.Identifier = identifier;
}
public bool Equals(InheritingClass obj)
=> this.Identifier.Equals(obj.Identifier);
public static explicit operator Type(EnumerateClass<Type, InheritingClass> obj)
=> obj.Identifier;
}
public sealed class MyNumber : EnumerateClass<int, MyNumber> {
private MyNumber(int identifier) : base(identifier) { }
public static readonly MyNumber None = new Number(0);
public static readonly MyNumber One = new Number(1);
public static readonly MyNumber Two = new Number(2);
...
public override string ToString() {
switch (this.Identifier) {
case 0: return "None";
case 1: return "One";
case 2: return "Two";
...
}
}
}
}
You could do this:
public static string ToOrderStatusDisplayString(this Enum status)
{
switch ((OrderStatus)status)
{
...
}
}
Then restrict TEnum to Enum: where TEnum : System.Enum
Of course, that way you get a bunch of methods on the Enum itself and lose type safety.

Categories

Resources