I usually access enum description for a corresponding value like:
Enum.GetName(typeof(MyEnum), myid);
I need to have an enum that could use any chars like "hello world %^$£%&"
I've seen people attaching an attribute and adding extensions like here:
http://weblogs.asp.net/stefansedich/archive/2008/03/12/enum-with-string-values-in-c.aspx
but I can't work out if this can be used to access the long description.
Anyone done anything similar?
Thanks
Davy
Why can't it work out?
You can create your own attribute by inherting from Attribute
public class EnumInformation: Attribute
{
public string LongDescription { get; set; }
public string ShortDescription { get; set; }
}
public static string GetLongDescription(this Enum value)
{
// Get the type
Type type = value.GetType();
// Get fieldinfo for this type
FieldInfo fieldInfo = type.GetField(value.ToString());
// Get the stringvalue attributes
EnumInformation [] attribs = fieldInfo.GetCustomAttributes(
typeof(EnumInformation ), false) as EnumInformation [];
// Return the first if there was a match.
return attribs != null && attribs.Length > 0 ? attribs[0].LongDescription : null;
}
public static string GetShortDescription(this Enum value)
{
// Get the type
Type type = value.GetType();
// Get fieldinfo for this type
FieldInfo fieldInfo = type.GetField(value.ToString());
// Get the stringvalue attributes
EnumInformation [] attribs = fieldInfo.GetCustomAttributes(
typeof(EnumInformation ), false) as EnumInformation [];
// Return the first if there was a match.
return attribs != null && attribs.Length > 0 ? attribs[0].ShortDescription : null;
}
Your Enum would look like this
public enum MyEnum
{
[EnumInformation(LongDescription="This is the Number 1", ShortDescription= "1")]
One,
[EnumInformation(LongDescription = "This is the Number Two", ShortDescription = "2")]
Two
}
You can use it this way
MyEnum test1 = MyEnum.One;
Console.WriteLine("test1.GetLongDescription = {0}", test1.GetLongDescription());
Console.WriteLine("test1.GetShortDescription = {0}", test1.GetShortDescription());
It outputs
test1.GetLongDescription = This is the Number 1
test1.GetShortDescription = 1
You can actually add properties to the attribute to have all kinds of information. Then you could support the localization you are looking for.
What do you mean by "long description"? I've got a library which allows you to attach Description attributes to enum values and fetch them:
public enum Foo
{
[Description("This is a really nice piece of text")]
FirstValue,
[Description("Short but sweet")]
Second,
}
If you're talking about the XML documentation, that's a different matter - that doesn't get built into the binary, so you'd have to build/ship the XML as well, and then fetch it at execution time. That's doable, but I don't have code to do it offhand...
I tend to stay away from this practice. If you think about it, it's binding your code's logic to how you typed your code. It would be much better to use a switch statement, resource file, database, etc...
I learned this the hard way. I had an app that we ultimately decided to obfuscate to help secure our code. As you can imagine, our binaries stopped working the way we wanted due to the enums be renamed during the obfuscation.
If you need an easy way to extract a description attribute for an enum value, have a look at my answer to a similar question
You just need to call the GetDescription extension method on the value :
string description = myEnumValue.GetDescription();
The Unconstrained Melody library mentioned by Jon includes a similar extension method (among many other cool things), you should check it out.
Related
I want to use the method in this article to implement friendlier ToString() outputs for my enum types. I would like to know how this can be done in Entity Framework's auto generated Enum codes? Would I have to modify the code generation template (if so, can someone kindly give me some guidance since the template is rather complicated), or can someone suggest an alternative method?
Thanks!
You can use your own enum type in your EF model, instead of creating a new enum in the model designer. Here's how:
In the model designer, rght click on surface and select:
Add New -> Enum Type...
In the dialog, just set checkbox:
Reference external type
and enter your type: namespace.MyEnum
Then create columns in your tables to use this type.
Since you're likely going to modify the existing model, make sure there is no confusion between enum type from the model and (external) enum type from your code. Best approach would be to remove the enum type you previously had created in the model and adjust the columns to use the associated enum type from your code.
Now, you can declare your enum type with description attributes as you planned.
You don't need to make workarounds for enums. They're supported in the latest Entity Framework.
To make your enums friendly to your website you can use attributes. Here is sample attribute:
public class EnumDescription : Attribute
{
public string Text { get; private set; }
public EnumDescription(string text)
{
this.Text = text;
}
}
Mark your enums with attribute:
public enum DaylightSavingTime
{
[EnumDescription("Detect automatically")]
Auto = 0,
[EnumDescription("DST always on")]
AlwaysOn = 1,
[EnumDescription("DST always off")]
AlwaysOff = 2
}
Add extensions to enable ToDescription() method:
public static class EnumExtensions
{
public static string ToDescription(this Enum enumeration)
{
Type type = enumeration.GetType();
MemberInfo[] memInfo = type.GetMember(enumeration.ToString());
if (null != memInfo && memInfo.Length > 0)
{
object[] attrs = memInfo[0].GetCustomAttributes(typeof(EnumDescription), false);
if (null != attrs && attrs.Length > 0)
return ((EnumDescription)attrs[0]).Text;
}
return enumeration.ToString();
}
}
Usage:
var blabla = DaylightSavingTime.Auto;
Console.WriteLine(blabla.ToDescription());
Output:
Detect automatically
I have enum:
enum MyEnum{
aaaVal1,
aaaVal2,
aaaVal3,
}
I need to have abbreviated version of 'MyEnum' which maps every item from 'MyEnum' to different values. My current approach is method which simply translates every item:
string translate(MyEnum myEnum)
{
string result = "";
switch ((int)myEnum)
{
0: result = "abc";
1: result = "dft";
default: result = "fsdfds"
}
return result;
}
the problem with this approach is that every time programmer changes MyEnum he should also change translate method.
This is not a good way of programming.
So..
Is there any more elegant solution for this problem?
Thank you :-)
Four options:
Decorate your enum values with attributes, e.g.
enum MyEnum
{
[Description("abc")]
AaaVal1,
[Description("dft")]
AaaVal2,
AaaVal3,
}
Then you can create a mapping (like the dictionary solution below) via reflection.
Keep the switch statement but switch on the enum value instead of a number for better readability:
switch (myEnum)
{
case MyEnum.AaaVal1: return "abc";
case MyEnum.AaaVal2: return "dft";
default: return "fsdfds";
}
Create a Dictionary<MyEnum, string>:
private static Dictionary<MyEnum, string> EnumDescriptions =
new Dictionary<MyEnum, string>
{
{ MyEnum.AaaVal1, "abc" },
{ MyEnum.AaaVal2, "dft" },
};
You'd need to handle the defaulting in the method, of course.
Use a resource file, with an entry for each string representation. This would be better if you're really trying to translate in a way that might need different translations for different cultures.
Considering that the use of descriptors on enums is quite common, here it's a good-enough class to do it:
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
class EnumDescriptor : Attribute
{
public readonly string Description;
public EnumDescriptor(string description)
{
this.Description = description;
}
public static string GetFromValue<T>(T value) where T : struct
{
var type = typeof(T);
var memInfo = type.GetField(value.ToString());
var attributes = memInfo.GetCustomAttributes(typeof(EnumDescriptor), false);
if (attributes.Length == 0)
{
return null;
}
return ((EnumDescriptor)attributes[0]).Description;
}
}
enum MyEnum
{
[EnumDescriptor("Hello")]
aaaVal1,
aaaVal2,
aaaVal3,
}
string translate(MyEnum myEnum)
{
// The ?? operator returns the left value unless the lv is null,
// if it's null it returns the right value.
string result = EnumDescriptor.GetFromValue(myEnum) ?? "fsdfds";
return result;
}
I'm finding what you're trying to do a bit weird.
If you're making translations, then you should create a RESX file and create ACTUAL translations.
But to answer your question, I guess you could create another enum with the same amount of fields and same numbering (if you're using anything other than the default) and have that act as the abbreviated names. Connecting one to the other should be straightforward:
string GetAbbreviation(Enum1 enum1)
{
return ((Enum2)((int)enum1)).ToString();
}
Attributes will be nice solution for this case. You can specify translations for enumeration members via declarative way:
public class TranslateAttribute
{
public string Translation { get; private set; }
public TranslateAttribute(string translation)
{
Translation = translation;
}
}
enum MyEnum
{
[Translate("abc")]
aaaVal1,
[Translate("dft")]
aaaVal2,
[Translate("fsdfds")]
aaaVal3
}
After this you should write common method for obtaining translations. It should check attribute with translation (via reflection) and return translation if it was specified and default value in other cases.
I am writing a wrapper to a REST API. So, whenever I am retrieving items from the server, I just need to play around with the parameters (in the uri). The way I am doing it now works fine but I just feel there must be another elegance way to do this maybe with enum or something.
I don't like that I need to 'know' what are the options are as the Dictionary type is string. I tried with Dictionary<EnumType, string>, but I have more than one type of enum. Plus, I am not sure how to map the EnumType (key) to the appropriate value.
Basically, I am trying to avoid as much as possible the use of magic keyword.
Here is my partial code:
public string GetUnreadItems()
{
var options = new Dictionary<string, string>();
options.Add("ItemType", "Unread");
options.Add("SortBy", "Latest");
// GetItemsBasedOn(options);
}
public string GetAllItems()
{
var options = new Dictionary<string, string>();
options.Add("ItemType", "All");
// GetItemsBasedOn(options);
}
public string GetItemsBasedOn(Dictionary<string, string> options)
{
// Do request here based on options passed
// and return the result to caller function
}
EDIT:
This is what I am trying to implement http://getpocket.com/developer/docs/v3/retrieve
I would like to implement the options sort, detailType, contentType, Favorite, and State. And each of the options have their own options but only one can be selected at a time.
There are a few ways you could go about improving your current design. I don't agree that the solution I am about to present to you is the ideal solution, but given that you have already identified that you would like to use an enum I think you will be content with this solution.
What you can do is define an enum marked with the [Flags] attribute. Assign to each flag in the enum a value that is a power of two. If you want to combine options to create a single option use bitwise or just like I did with the flag named "All" in the proceeding sample:
[Flags]
public enum GetItemOptions
{
Read = 0x1,
Unread = 0x2,
All = 0x1 | 0x2,
SortByOldest = 0x4,
SortByLatest = 0x8
}
From your code sample, the first call will now look like this:
GetItemsBasedOn(GetItemOptions.Unread | GetItemOptions.SortByLatest);
And the second will look like this:
GetItemsBasedOn(GetItemOptions.All);
In order to enable this design you will need to adjust your GetItemsBasedOn method signature so that it specifies an argument of the GetItemOptions enum type. Below is an example of how you can handle different settings.
public static void GetItemsBasedOn(GetItemOptions getItemOption)
{
if (getItemOption.HasFlag(GetItemOptions.SortByOldest) && getItemOption.HasFlag(GetItemOptions.SortByLatest))
throw new ArgumentException("I can't sort by both...");
if (getItemOption.HasFlag(GetItemOptions.Read))
{
Console.WriteLine("READ");
}
if (getItemOption.HasFlag(GetItemOptions.Unread))
{
Console.WriteLine("UNREAD");
}
if (getItemOption.HasFlag(GetItemOptions.SortByOldest))
{
Console.WriteLine("SORT BY OLDEST");
}
else if (getItemOption.HasFlag(GetItemOptions.SortByLatest))
{
Console.WriteLine("SORT BY NLATEST");
}
}
I don't think you know much about bit-wise operations, and for that reason I simplified the code sample as much as possible by utilizing the Enum.HasFlag method which simply checks if the given GetItemOptions enum has a flag specified.
You may have bared witness to this pattern before when using RegexOptions Enumeration or ControlStyles Enumeration
Update
I would suggest that you create an enum for each parameter and define a class like this:
public class PocketDataRequest
{
public State? State { get; set; }
public Favourite? Favourite { get; set; }
public ContentType? ContentType { get; set; }
public Sort? Sort { get; set; }
public DetailType? DetailType { get; set; }
public Dictionary<string, string> ToPostData()
{
return GetType().GetProperties()
.Where(p => p.GetValue(this, null) != null)
.ToDictionary(p => p.Name,
p => p.GetValue(this, null).ToString());
}
}
This would leverage the following syntax:
PocketDataRequest pocketDataRequest = new PocketDataRequest();
pocketDataRequest.State = State.Unread;
pocketDataRequest.Sort = Sort.Newest;
GetItemsBasedOn(pocketDataRequest.ToPostData());
In my implementations ToPostData method, I use LINQ and Reflection, that is just because I am lazy. You need to manually evaluate each enum value, especially if you want to change the enum names to something more appropriate. Also, my code will fail if you try and pass the parameter titled favorite. This is because favorite takes either the number "0" or "1". This is not a big problem because what you can do it define the enum like this:
public enum Favourite
{
UnfavouritedItems = 0,
FavouritedItems = 1
}
and then simply cast the value (Int32) and add that value to the Dictionary<string, string> or NameValueCollection.
I'm creating this selectbox in a SharePoint web part and need to have a drop down with the current version so I need to use an Enum.
public enum SelectVersionEnum { 2010, 2007 };
Well you can see where it breaks, is there any way to use integers in a enum?
Most of all I'd like to use
public enum SelectVersionEnum { 2010=14, 2007=12 };
No, you can not name enums with integer names.
An enum value name is a normal identifier and must follow the same rules as everything else.
You can, however, use:
public enum SelectVersionEnum
{
Version2007 = 12,
Version2010 = 14
}
Additionally, Enum.Parse can parse strings with integers into their corresponding enum value, even if value described in the string doesn't exist.
Try the following in LINQPad:
void Main()
{
Enum.Parse(typeof(SelectVersionEnum), "12").Dump();
Enum.Parse(typeof(SelectVersionEnum), "14").Dump();
Enum.Parse(typeof(SelectVersionEnum), "2007").Dump();
}
public enum SelectVersionEnum
{
Version2007 = 12,
Version2010 = 14
}
The output:
Version2007
Version2010
2007
What do you think would've happened if you defined the following:
public enum SelectVersionEnum
{
12 = 14,
14 = 16
}
Does the string "14" now mean "12" or "14"?
Enum members must be valid C# identifiers.
They cannot start with numbers.
Instead, you can use something like Office2007, Office2010 or V2007, V2010.
No, enum identifiers can't start with a numeric character.
You can add an extension method to your enum like any other type.
So you could create an extension for your SelectVersionEnum to help you get the enum values in the right format...
public static class SelectVersionEnumExtension
{
public static int Version(this SelectVersionEnum enumValue)
{
return 0; // Obviously you should return something meaningful here..
}
}
This gives you a lot of flexibilty.
One way you could have numeric values associated with enums is by using the description attribute. For example you might have the enum:
[Serializable]
public enum SelectVersionEnum
{
[Description("2007")]
v2007,
[Description("2010")]
v2010
}
You could then write an extension method to get the numeric value you are looking for.
public static string Description(this Enum value)
{
var type = value.GetType();
var name = Enum.GetName(type, value);
if (name != null)
{
if (type.GetField(name) != null)
{
var attr = Attribute.GetCustomAttribute(type.GetField(name), typeof(DescriptionAttribute)) as DescriptionAttribute;
return attr != null ? attr.Description : name;
}
}
return null;
} // end
You would use it like this:
var version = SelectVersionEnum.v2007.Description();
is it possible to make a default typecast for an enum?
I use enum for a lot, such as states and I want to compare enums directly to LINQ fields, but I have to typecast all the time.
You should be able to have properties in your LINQ objects that have an enum type. This way you do not have to cast.
So just change your properties to have the correct enum type and you don't have to worry about casts any longer. You can do this in the LINQtoSQL designer. Just right-click on a property, select 'Properties' and set the appropriate Type in the Visual Studio Properties window.
The answer was MUCH more simple!!!
A good friend of mine told me this is very simple! have a look at this sample!
public enum State:byte
{
EmailNotValidated = 0x00,
EmailValidated = 0x10,
Admin_AcceptPending = 0x40,
Active = 0x80,
Admin_BlockedAccount = 0xff
}
Pay attention to the :BYTE part after the name of the Enum... there is the trick I was looking for! But thanks to everyone trying for me!
LINQ-to-SQL will usually handle direct integer maps and exact string (name) maps (note: case sensitive). Meaning: write your enum somewhere, and in the designer set the property type as the fully-qualified enum name: Some.Namespace.MyEnum. It should usually work.
For non-trivial mappings (for example where the column is a varchar with mixed-case values, or things like "In Progress" [note the space]), you will have to leave the storage property as int/varchar (etc) and map it manually. I usually do this by marking it as private and naming it FooStorage, and adding a mapping property in a partial class:
partial class MyType {
public MyEnum Foo {
get {... mapping logic reading from FooStorage...}
set {... mapping logic, updating FooStorage...}
}
}
The only problem is that LINQ queries will only work against the storage property (not the bespoke property).
Have you tried extension methods?
public enum MyEnum
{
First = 1,
Second = 2,
Third = 3
}
public static class Utility
{
public static string Description(this Enum e)
{
Type t = e.GetType();
DescriptionAttribute[] desc =
(DescriptionAttribute[])(t.GetField(e.ToString())
.GetCustomAttributes(typeof(DescriptionAttribute), false));
return desc.Length > 0 ? desc[0].Description : e.ToString();
}
public static byte ToByte(this Enum ai)
{
object o=Enum.ToObject(ai.GetType(), ai);
return Convert.ToByte(o);
}
}
class Program
{
static void Main(string[] args)
{
MyEnum me = MyEnum.Third;
Console.WriteLine("Value: {0}\r\nType: {1}"
,me.ToByte(),me.ToByte().GetType().ToString());
Console.ReadLine();
}
}
It outputs:
Value: 3
Type: System.Byte