So far I have soemthing like this:
public enum MyEnum
{
[Display(Name="FirstElement")]
element1 = 1,
[Display(Name="SecondElement")]
element2 = 2
}
And in the view, if I use:
Html.EnumDropDownListFor(x=>x.EnumProperty)
While having:
public MyEnum EnumProperty {get;set;}
On the view model, the dropdown works as expected(I see FirstElement, and the value attached is element1)
The problem is that I want to have a dash in the value, like this:
public enum MyEnum
{
[Display(Name="FirstElement")]
element1 = 1,
[Display(Name="SecondElement")]
element2 = 2,
[Display(Name="Third")]
element-dashed = 3
}
It won't work, as element-dashed is not a valid entry.
This won't work either:
[Value("element-dashed")]
[Display(Name="Third")]
elementDashed = 3
The answer to your question is simple. But it warrants an explanation, to explain why this is the answer.
There is no functional reason to want a dash in an enum.
Let's say I have a working application, which contains your enum:
public enum MyEnum
{
[Display(Name="FirstElement")]
element1 = 1,
[Display(Name="SecondElement")]
element2 = 2
}
Suppose I change the enumerator names:
public enum MyEnum
{
[Display(Name="FirstElement")]
BatmanIsBruceWayne = 1,
[Display(Name="SecondElement")]
SupermanIsClarkKent = 2
}
I am assuming that every usage of the enum in code is also adjusted accordingly. If you use the Rename method in VS (F2), this is automatically the case.
Nothing has changed about how the application works. Functionally speaking, the name of an enum does not matter.
public enum CardSuit { Hearts, Spades, Diamonds, Clubs }
public enum CardSuit { TheRedOne, TheBlackOne, TheOtherRedOne, TheOtherBlackOne }
Of course, the first one is much more readable for developers than the second one. However, a compiler does not care about the meaning of the enum names. It only cares that the appropriate value is used consistently.
This is the same principle as variable names. While we should of course take care to make variable names readable by developers; there is no technical reason (i.e. to the functionality of the application) why we should use int myDescriptiveInteger over int x.
We only choose a descriptive name for the purposes of readability for humans who maintain the code.
There is a single exception to this: If you are calling .ToString() on an enum, in order to have a string representation of the field you are using.
Note: While this is technically possible and has been done before by others; I do wonder if that's really what you should be doing here. You're already assigning explicit int values to the enum values. Those int values are much easier for saving/loading an enum instead of trying to work with its string equivalent. While both are technically possible, using the int is far better due to a smaller footprint and a lack of the naming problem that you are wrongly trying to fix here.
It is not clear from your question whether this is the case for you. Maybe it's done somewhere in your application, maybe it's not. I can't know that without you telling me.
If it is not done, then your problem is functionally irrelevant. The name does not matter except for readability purposes, and I'm quite convinced that your developers won't suddenly stop understanding the intent of the code because there's a dash missing.
If it is done, then you actually have already provided the answer to your problem. Instead of calling .ToString() on the enum; instead you must use the value of [Display(Name="FirstElement")] as the correct "string name" for your enum.
public static string GetName(this myEnum enumValue)
{
return enumValue.GetType()
.GetMember(enumValue.ToString())
.GetCustomAttribute<DisplayAttribute>()
.GetName();
}
And then you can change the following code:
myEnumValue.ToString();
To the following code, which correctly uses the attribute's value instead of the enum field name:
myEnumValue.GetName();
In both cases, the core answer remains the same: You do not need to worry about the specific name of your enum fields because it is functionally irrelevant.
Edit If you want your enum value to be displayed differently in the combobox than you want it to be when you save the value, then use a second attribute to differentiate between the two. But the principle still stands: do not base yourself off of the field names than an enum uses.
And I would like to stress again that you should be using the integer values of your enum. I see no reason why you would explicitly assign int values to your enum and then refuse to use them.
Related
I am lately starting a project and I have a question.
Let's say I am dealing with a class Person, and a person can have one(or more) deseases he is encountering.
so I have created an enum :
public enum diseases{headache,throat,bruise,gunshot,none}; // enum containing all the diseases
public diseases disease;
And further in code I set a certain disease to that person and it works fine.
Thing is, there might be a point in my project where a person might have 2 diseases.
So there are my questions:
Is using enum the best option here? I want my code to be organized and understood and that's a main reason for using enums.
If using enum is a good option, I have managed to combine this enum with bit-flags(using [System.Flags]) so when time comes I can check for a disease that contains two different values from the enum. is this a good approach?
If using enum is a good option, should I just create a second property from diseases (just like I created disease) and save all the trouble from using bit-flags?
Thanks in advance for any light on that matter, couldn't figure what was the best approach here.
A good option would to make a List<diseases> to hold for a single person.
public class Person
{
public string Name { get; set; }
public List<diseases> Diseases { get; set; }
public Person(string name)
{
this.Name = name;
Diseases = new List<diseases>();
}
}
This way you can enumerate over all the values relatively easily without having to worry about flags.
For example:
var bob = new Person("bob");
bob.Diseases.Add(diseases.gunshot);
var hasHeadache = bob.Diseases.Any(x => x == diseases.headache);
An enum is a plausible (yet a bit simplistic) way to represent one disease.
If someone may have N diseases, then just use a container of objects of that type, such as a list. But you need to choose the right container. A list of diseases may be, for example: { headache, throat, headache, throat, gunshot }. Lists allow duplicates. Whay you may actually need is a set of diseases. A set is a structure which does not allow duplicates.
The choice of how you represent one disease and the fact that a person may have N diseases, so that you need a person to have a container of diseases, are two totally independent facts.
Use the FlagsAttribute on your enum.
[Flags]
Public enum diseases
{
// your values here
}
This should give you what you need.
There is a lot of opinion being asked for here and the right answer is that it depends on a lot of variables what solution is right in any given situation. You CAN use an ENUM to represent multiple values so long as the [Flags] attribute is given to the ENUM. Keep in mind that if you decide to go that route then you are responsible for assigning the values of the ENUM specific non-overlapping values like 1, 2, 4, 8, 16, 32, etc.
Is using enum the best option here? - This depends on how many diseases you would like to cover. If the number is small (10..20) and the list is flat (no hierarchy of diseases) then you are fine. Otherwise, consider some alternative.
is [System.Flags] a good approach? - Yes, in case of a small, flat, list [System.Flags] approach is very good and extremely efficient.
Should I just create a second property from diseases and save all the trouble from using bit-flags? Having a second property in place of running a collection is a terrible idea. If you have to do something twice, chances are, you'd do it N times. Make a collection or bit fields, never go for a second property unless the system is inherently limited to two items (say, a binary tree).
If the list of diseases is not "flat" (e.g. you plan to have ViralThroat, BacterialThroat, and ThroatInjury in place of a simple throat that you have now) you would be better off modeling diseases as classes, and creating a collection of diseases attached to a Person. You could add methods for checking the collection for particular diseases, which could be smart about the search: for example, find any kind of throat sub-disease when a throat is passed as a search criterion.
enum is just one of many perfectly acceptable options here. Others include OO solutions such as base/derived classes, or simple lists of string (or event lists of enums, why not?!). Often the simplest approach is best. We would have to know a lot more about your problem to recommend one over the other.
While using Flags is one solution, I would not recommend it in this case. Flags are not verbose on what they are for. Any time I used flags, I would have to re-learn how to properly work with flags when I needed to modify my code. One simple alternative is creating a property for each possible disease...
public class Diseases
{
public bool Headache {get;set;}
...
public bool HasAnyDisease() { return Headache || Throat || ...;}
}
But that has it's downsides as well. It's not easily extensible. I would recommend using the Decorator Pattern. Each disease could decorate the class which may make future interactions with it easier to maintain. If you need to have variance disease combinations cause different outcomes, this may be better in the long run.
I'm trying to enhance my enum so I've tried a suggestion on Display and another one on Description.
I'm annoyed because I don't understand the difference between them. Both Description class and Display class are from framework 4.5.
It's additionally annoying since neither of them work in the code. I'm testing the following but I only get to see the donkeys...
[Flags]
public enum Donkeys
{
[Display(Name = "Monkey 1")]
Donkey1 = 0,
[Description("Monkey 2")]
Donkey2 = 1
}
Neither of these attributes have any effect on the enum's ToString() method, which is what gets called if you just try to insert it into a Razor template. ToString() always uses the name declared in code -- Donkey1 and Donkey2 in your case. To my knowledge, there's no built-in way to specify an alternate string representation for the enum to use automatically.
I assume there are (at least) two reasons for that:
Serialization. ToString() uses the name so that Enum.Parse() can parse it back into the enum.
Localization. .NET was designed with global audiences firmly in mind, and if you want a human-readable string representation of an enum, it's extremely unlikely that there will be just one string representation, at which point it's going to be up to your application to figure out how to do it.
If you know your app will never be translated to other languages, or if you just want a string representation you can use in debug output, you're welcome to use an attribute (either one from the Framework, or one you declare yourself) to define a string representation for each enum value, and write some utility functions to do the string conversion. But you can't make the enum's ToString() do it for you (since that would break serialization); you'd have to write your own code to do it.
However, since you're writing a Web app, there's a fair chance that you will have a global audience -- in which case you'll need to localize your enum strings the same way you localize all your other text.
someone wants me to make other people's code compliant to some FxCop ruleset which includes rule CA1726:Use preferred terms. Most of the terms/replacements are all right and I can understand that one has to decide on one single way to name things.
However, what's the deal with the term 'flags'? Can anyone explain to me why I shall not use this name? (before I go and complain about it at my boss ;) )
Say, I have a data object which has a member of class 'flags' which bundles a large number of properties that define how to handle the data object. How else would you call this?
In the book Framework Design Guidelines, which is what FxCop is based on, the authors say that using Flag or Flags is a bad idea. Their alternative suggestion is that when naming enumerations that you use a singular name for standard enums and a plural name for bit field (flags) enums.
For example if you wanted to create an enum listing different visibilities then you would name it Visibilities instead of VisibilityFlags or Visibility:
[Flags]
public enum Visibilities {
Public,
Private
}
The only items considered flags in .NET by the authors are these bitfield enumerations due to the keyword Flags attribute.
I would say that the property should be named aptly, and that the term Flags characterises the property rather than describing it.
Flag or Flags | There is no replacement term. Do not use.
For instance, Flags is generally used with enumerations (that are decorated with the appropriate attribute) and we certainly don't need to explicitly state so within the name / identifier of a property:
[Flags]
enum StorageMode
{
None = 0,
Next = 1,
...
Last = 32
}
class StorableItem
{
public StorageMode StorageMode { get; set; }
}
But, in your case, I get the feeling that whatever is named, or contains within its name, Flags, isn't actually a set of flags in the above sense - which just brings up another reason as to why to avoid it.
My application has a model which currently uses an integer in the SQL database to store the value of a [Flags]Enum. The Enum looks something like this:
namespace MyProject.Models
{
[Flags]
public enum MyEnum : int
{
FirstThing = 1,
SomethingElse = 2,
YetAnotherOne = 4
}
}
So if a particular row had this field set to 3, it means flags FirstThing and SomethingElse are both set. Right now I'm using a helper class to convert and check MyEnum values to/from/against the integer, which does work, but I think there's gotta be a way to map the SQL INT field directly to the enum.
Basically the end goal is to have a list of checkboxes, one for each possible flag that will eventually be saved in the database as an INT.
Is this a good idea? If so, how do I go about this? If not, should I just suck it up and write out all that code myself (instead of using some nifty tricks)?
You will need that helper class, neither OR mapper fully supports mapping int to enum. There are ways around it, but that's more of a replication of the target behaviour with gapping holes in it than anything near the wanted effect.
I've encourted a problem recently about cycling between constants of an enum class in .net (that is created from my OWL class, by Rowlex OwlGrinder). Problem was solved by means of using .net reflection (thanks to great help from dear Mr. Jon Skeet):
stackoverflow:problem-cycling-enum-class-values
By solving this, I started using it. After matching a dropDownList selected value to one of the enum class instances, I had to declare the selected object(=Language) to my RDF subject(=learningResource), via a predicate (=hasLanguage).
//learningResource is a new RDF subject, hasLanguage is predicate, and there
//is a new value for it - Language.
System.Reflection.FieldInfo[] resLanFields =
typeof(Language).GetFields();
for (int i = 0; i < resLangFields.Length; i++)
{
if (resLanFields[i].Name.Equals(dropDownList_lang.SelectedValue))
learningResource.hasLanguage = ??? //i-th constant of Language
}
Now the problem appears; I can not use Language[i] (or something like this to select i-th constant of Language class) to assign to hasLanguage. Is there a way to select i-th constant of an enum class (like refelections)?
Would any one please help me in this situation?
The Language class is not an enum in C# terminology. It is an ordinary class with public const string fields. ROWLEX intentionally generates enum-imitating-classes instead of native enums for 2 reasons:
The value of an native C# enum is an integer, while a public const string field can take the URI of the OWL class instance.
The class can have one additional public static string that is "URI" which represents the class URI consistently for every single ROWLEX generated class.
That was the background. If I understood your question right, you had an issue binding the selected name displayed in the dropdown back to the URI, and you wanted to use the position of the element inside the array you created. I would not do it that way. DropDownLists typically have both ID and Value fields for every list item (can be named differently component to component). The ID is expected to be a unique object while the Value is expected to contain something human readable. Set the ID with URI of the "enum" field, and the Value as you have done. So this is how you populate your dropdown:
System.Reflection.FieldInfo[] resLanFields = typeof(Language).GetFields();
foreach(FieldInfo field in resLanFields)
{
ListItem item = new ListItem();
item.ID = field.GetValue(null); // takes the URI value of the const field
item.Value = field.Name; // takes the name of the const field
dropDownList_lang.AddItem(item);
}
And when the user made his/her choice, this is how you read the result out:
learningResource.hasLanguage = (string) dropDownList_lang.SelectedItem.ID;
Since there is implicit casting operator implemented on the ROWLEX enum-imitating-class (string=>Language) you can safely set your triple subject as a string. Should compile without issue. Simple, isn't it?
;)
I'd say
resLanFields[i].GetValue(null)
but if this works, don't vote me up, this was in Jon's original answer.