Array of strings "subscripted" by an enum in C# - c#

This is a hang over from my Delphi days where I was able to do something as follows:
type
TCars = (Ford, Nissan, Toyota, Honda);
const
CAR_MODELS = array[TCars] of string = ('Falcon','Sentra','Camry','Civic');
which allowed me to an enumeration declartively with some associated data. In this case a string but it could have been a record structure or similair. It meant that if I added a member to TCars and forgot to update the CAR_MODELS array I would get a compile time error.
What is the C# approach to this? I have tried:
public enum ReportFileGeneratorFileType
{
Excel,
Pdf,
Word
}
string[ReportFileGeneratorFileType] myArray = {"application/vnd.ms-excel", "application/pdf", "application/vnd.ms-word"};
but that does not appear to compile.

You should use Dictionary<key, value> as
var myArray = new Dictionary<ReportFileGeneratorFileType, string>();
myArray[ReportFileGeneratorFileType.Excel] = "application/vnd.ms-excel";

You could use an attribute (custom or built-int, such as DisplayNameAttribute) over the enum values to give them associated names/values. Example custom attribute follows:
public class ContentTypeAttribute : Attribute
{
public string ContentType { get; set; }
public ContentTypeAttribute(string contentType)
{
ContentType = contentType;
}
}
public enum ReportFileGeneratorFileType
{
[ContentType("application/vnd.ms-excel")]
Excel,
[ContentType("application/pdf")]
Pdf,
[ContentType("application/vnd.ms-word")]
Word
}
To retrieve the content type based on an enum value, use:
...
ReportFileGeneratorFileType myType = ...
string contentType = myType.GetType()
.GetCustomAttributes(typeof(ContentTypeAttribute))
.Cast<ContentTypeAttribute>()
.Single()
.ContentType;
...
This may seem kind of complicated, but lets you keep content types (or whatever data) right on the enum itself, so you're not likely to forget adding it after adding a new type.

In c# you can use Attributes, using this namespace
using System.ComponentModel;
You can add Description attribute to your enum elements
public enum ReportFileGeneratorFileType
{
[Description("application/vnd.ms-excel")]
Excel,
[Description("application/pdf")]
Pdf,
[Description("application/vnd.ms-word")]
Word
}
And using these methods you can "extract" a Dictionary<ReportFileGeneratorFileType, string> from your enum and use it on your code.

Related

c# Newtonsoft.json custom Enum serialization

I need to customize the way Newtonsoft.Json serializes an object, in particular about Enum types.
Given a sample class like this:
public class TestEnumClass
{
public Enum ReferencedEnum { get; set; }
public string OtherProperty { get; set; }
public StringSplitOptions OtherEnum { get; set; }
}
The default serialization will happen this way:
var testEnumClass = new TestEnumClass
{
ReferencedEnum = StringComparison.OrdinalIgnoreCase,
OtherProperty = "Something",
OtherEnum = StringSplitOptions.None
};
var serialized = JsonConvert.SerializeObject(testEnumClass, Formatting.Indented);
And the serialized string will be:
{
"ReferencedEnum": 5,
"OtherProperty": "Something",
"OtherEnum": 0
}
Here I have 2 problems:
I cannot guarantee that the order of the Enums will remain the same (here I am using Enums included in the framework, but my project has other Enums that derive from ": Enum"), so I cannot keep the number as the serialized value of the Enum.
Secondly, and more important, is the fact that the "ReferencedEnum" field is declared as "Enum", and any kind of Enum can be written in that field (ReferencedEnum = AnyEnum.AnyEnumValue).
This leads to the fact that when deserializing the value, I need to know the original Enum type (in the example is StringComparison).
I was thinking of using a Converter (deriving from ": JsonConverter") and manipulating what is written and read. The result I was thinking was something like this:
{
"ReferencedEnum": {
"EnumType": "StringComparison",
"EnumValue": "OrdinalIgnoreCase"
},
"OtherProperty": "Something",
"OtherEnum": "StringSplitOptions.None"
}
I this way the deserializer would know:
for "Enum" properties, the original type and the string value.
for "typed Enum" (specific enum) properties, the full type and value.
What I cannot absolutely add is a reference to the converter in the model class like this:
[JsonConverter(typeof(EnumConverter))]
public Enum ReferencedEnum { get; set; }
And I also would avoid to have the "$type" field in the serialized string (except if this is the only solution).
By the way, I can add a generic attribute like this:
[IsEnum]
public Enum ReferencedEnum { get; set; }
Does somebody have any idea of how can I get the result needed?
Thank you!
I've been in the very same issue and developed a nuget package named StringTypeEnumConverter, that solves it.
You can check the project here.
The usage will be as simple as any other converter.
This converter derives from an already existing "StringEnumConverter", that writes the string value of the enum, instead of its numeric counterpart.
I added the support to writing the type name too.
The result will be like: "StringSplitOptions.None" (this is a string value, not an object).
Note that this converter should be applied for both writing and reading, as the resulting json would not be compatible with other readers not including this converter.
You should consider using this package only if you cannot avoid using enums in your model.
I would also suggest you to spend time to check if (custom) enums could be transformed to classes.
J.

Best way to pass custom options to function

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.

Cannot add manual anonym items to IQueryable

fromI have this linq query to build a json (of string and bool?) from an IQueryable :
var ret = from c in results select new { country = c.EN, schengen = c.Schengen };
I would like to append new items to it manually : (pseudo code)
ret = ret /* add this -> */ { country = "some new country", schengen = true }
I've tried to do that :
//End up with cannot convert from AnonymousType#2 to AnonymousType#1
var listRet = ret.ToList();
listRet.Add(new { country ="", schengen = true });
As it is an anonymous type that I build I cannot find a way to add it. I always end up with conversion error
Anonymous types are still statically typed and C# compiler generates a separate class for each anonymous type definition occurrence. You need a named class in this case. It's a good practice for data transfer objects anyway. I use JSON DTOs like this, using data annotations and DataContractJsonSerializer.
[DataContract]
public class CountryInfo
{
[DataMember(Name = "country")]
public string Country { get; set; }
[DataMember(Name = "schengen", EmitDefaultValue = false)
public bool? Schengen { get; set; }
}
This way I have a well-documented JSON "protocol", which also uses C# naming conventions before serialization, but JS naming conventions after.
Well it's obvious. Generics are invariant. So another type won't fit. You can use List<object> or a named class to store the values.
If you want to change the result manually, you need to cast it into a static type instead of a dynamic one. Create a class that has a string country and bool schengen(You can even create the class within your current class to hide it from the rest of the application, if that's applicable), cast your linq results into that and then just add to it.

Can I make an Enum return something other than an integer?

If I have the following:
public enum TYPE
{
One = 1,
Two = 2,
Three = 3
}
When I do:
var a = TYPE.One;
I would like it to populate the variable a with a string in the format "01". In other words two digits with a leading zero.
Is it possible to do this by assigning some method to the SomeEnum? I realized I could use TYPE.One.ToString("00")but I would like to have it self-contained in the enum and something very simple to use.
You can add a string to each enum element using the description attribute.
e.g.
public Enum MyEnum
{
[Description("Value A Description")]
ValueA,
[Description[("Value B Description")]
ValueB
}
To retrieve the description value, use an extender class
public static class MyEnumExtender
{
public static string Description(this Enum Value)
{
FieldInfo FI = Value.GetType().GetField(Value.ToString());
IEnumerable<DescriptionAttribute> Attributes = FI.GetCustomAttributes(typeof(DescriptionAttribute), false).Cast<DescriptionAttribute>();
return (Attributes.Any()) ? Attributes.First().Description : Value.ToString();
}
}
....
MyEnum EnumVar = MyEnum.ValueA;
string Description = EnumVar.Description();
can do something like this :
public static class Ext {
public static string ToMyString(this Enumer en ) {
return ((int)en).ToString("00");
}
}
and after use this like:
public enum TYPE { One = 1, Two = 2, Three = 3 }
Type t = TYPE.One;
string s = t.ToMyString();
Yes, conceptually it's the same as like declaring a string , but it's hidden inside extension method.
Other solution is: to simply avoid, at this point, using enums in that way.
Don't use enums for that, but something like a Dictionary or Hash. Enums are there when there is a limited set of possibilities and you do not want or need a value. How it is stored is irrelevant.

How to set space on Enum

I want to set the space on my enum. Here is my code sample:
public enum category
{
goodBoy=1,
BadBoy
}
I want to set
public enum category
{
Good Boy=1,
Bad Boy
}
When I retrieve I want to see Good Boy result from the enum
You can decorate your Enum values with DataAnnotations, so the following is true:
using System.ComponentModel.DataAnnotations;
public enum Boys
{
[Display(Name="Good Boy")]
GoodBoy,
[Display(Name="Bad Boy")]
BadBoy
}
I'm not sure what UI Framework you're using for your controls, but ASP.NET MVC can read DataAnnotations when you type HTML.LabelFor in your Razor views.
Here' a Extension method
If you are not using Razor views or if you want to get the names in code:
public class EnumExtention
{
public Dictionary<int, string> ToDictionary(Enum myEnum)
{
var myEnumType = myEnum.GetType();
var names = myEnumType.GetFields()
.Where(m => m.GetCustomAttribute<DisplayAttribute>() != null)
.Select(e => e.GetCustomAttribute<DisplayAttribute>().Name);
var values = Enum.GetValues(myEnumType).Cast<int>();
return names.Zip(values, (n, v) => new KeyValuePair<int, string>(v, n))
.ToDictionary(kv => kv.Key, kv => kv.Value);
}
}
Then use it:
Boys.GoodBoy.ToDictionary()
You are misunderstanding what an enum is used for. An enum is for programming purposes, essentially giving a name to a number. This is for the programmer's benefit while reading the source code.
status = StatusLevel.CRITICAL; // this is a lot easier to read...
status = 5; // ...than this
Enums are not meant for display purposes and should not be shown to the end user. Like any other variable, enums cannot use spaces in the names.
To associate internal values with "pretty" labels you can display to a user, can use a dictionary or hash.
myDict["Bad Boy"] = "joe blow";
using System.ComponentModel;
then...
public enum category
{
[Description("Good Boy")]
goodboy,
[Description("Bad Boy")]
badboy
}
Solved!!
Think this might already be covered, some suggestions:
How do I have an enum bound combobox with custom string formatting for enum values?
C# Getting Enum values
Just can't beat stackoverflow ;) just sooo much on here nowdays.
Based on Smac's suggestion, I've added an extension method for ease, since I'm seeing a lot of people still having issues with this.
I've used the annotations and a helper extension method.
Enum definition:
internal enum TravelClass
{
[Description("Economy With Restrictions")]
EconomyWithRestrictions,
[Description("Economy Without Restrictions")]
EconomyWithoutRestrictions
}
Extension class definition:
internal static class Extensions
{
public static string ToDescription(this Enum value)
{
FieldInfo field = value.GetType().GetField(value.ToString());
DescriptionAttribute attribute = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;
return attribute == null ? value.ToString() : attribute.Description;
}
}
Example using the enum:
var enumValue = TravelClass.EconomyWithRestrictions;
string stringValue = enumValue.ToDescription();
This will return Economy With Restrictions.
Hope this helps people out as a complete example. Once again, credit goes to Smac for this idea, I just completed it with the extension method.
That's not possible, an enumerator cannot contain white space in its name.
Developing on user14570's (nice) workaround referred above, here's a complete example:
public enum MyEnum
{
My_Word,
Another_One_With_More_Words,
One_More,
We_Are_Done_At_Last
}
internal class Program
{
private static void Main(string[] args)
{
IEnumerable<MyEnum> values = Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>();
List<string> valuesWithSpaces = new List<string>(values.Select(v => v.ToString().Replace("_", " ")));
foreach (MyEnum enumElement in values)
Console.WriteLine($"Name: {enumElement}, Value: {(int)enumElement}");
Console.WriteLine();
foreach (string stringRepresentation in valuesWithSpaces)
Console.WriteLine(stringRepresentation);
}
}
Output:
Why don't you use ToString() ?
I mean that when use ToString(),it gives the enum value. Just you have to add some identifier to catch space.For example:
public enum category
{
good_Boy=1,
Bad_Boy
}
When you get an enum in codes like category a = ..., you can use ToString() method. It gives you value as a string. After that, you can simply change _ to empty string.
I used Regex to split the values by capital letter and then immediately join into a string with a space between each string in the returned array.
string.Join(" ", Regex.Split(v.ToString(), #"(?<!^)(?=[A-Z])"));
First get the values of the enum:
var values = Enum.GetValues(typeof(Category));
Then loop through the values and use the code above to get the values:
var ret = new Dictionary<int, string>();
foreach (Category v in values)
{
ret.Add((int)v, string.Join(" ", Regex.Split(v.ToString(), #"(?<!^)(?=[A-Z])")));
}
In my case I needed a dictionary with the value and display name so that it why I have the variable "ret"
C# now has a built in function to get the description from an enum. Here's how it works
My Enum:
using System.ComponentModel.DataAnnotations;
public enum Boys
{
[Description("Good Boy")]
GoodBoy = 1,
[Description("Bad Boy")]
BadBoy = 2
}
This is how to retrieve the description in code
var enumValue = Boys.GoodBoy;
string stringValue = enumValue.ToDescription();
Result is : Good Boy.
public enum MyEnum { With_Space, With_Two_Spaces } //I store spaces as underscore. Actual values are 'With Space' and 'With Two Spaces'
public MyEnum[] arrayEnum = (MyEnum[])Enum.GetValues(typeof(MyEnum));
string firstEnumValue = String.Concat(arrayEnum[0].ToString().Replace('_', ' ')) //I get 'With Space' as first value
string SecondEnumValue = String.Concat(arrayEnum[1].ToString().Replace('_', ' ')) //I get 'With Two Spaces' as second value
If you do not want to write manual annotations, you can use an extension method that will add spaces to the enum's names:
using System.Text.RegularExpressions;
public static partial class Extensions
{
public static string AddCamelSpace(this string str) => Regex.Replace(Regex.Replace(str,
#"([^_\p{Ll}])([^_\p{Ll}]\p{Ll})", "$1 $2"),
#"(\p{Ll})([^_\p{Ll}])" , "$1 $2");
public static string ToCamelString(this Enum e) =>
e.ToString().AddCamelSpace().Replace('_', ' ');
}
You can use like this:
enum StudentType
{
BCStudent,
OntarioStudent,
badStudent,
GoodStudent,
Medal_of_HonorStudent
}
StudentType.BCStudent.ToCamelString(); // BC Student
StudentType.OntarioStudent.ToCamelString(); // Ontario Student
StudentType.badStudent.ToCamelString(); // bad Student
StudentType.GoodStudent.ToCamelString(); // Good Student
StudentType.Medal_of_HonorStudent.ToCamelString(); // Medal of Honor Student
See on .NET fiddle
Retrieving enum value is pretty complicated to me, while enum value is different than its name. For this purpose, I would like to use a class with const fields and a list that contains all of these fields. Using this list, I could check later to validate.
public class Status
{
public const string NOT_STARTED = "not started";
public const string IN_PROGRESS = "in progress";
public const string ON_HOLD = "on hold";
public const string COMPLETED = "completed";
public const string REFUSED = "refused";
public static string[] List = new string[] {
NOT_STARTED,
IN_PROGRESS,
ON_HOLD,
COMPLETED,
REFUSED
};
}
class TestClass {
static void Main(string[] args) {
var newStatus = "new status"
if (!Status.List.Contains(newStatus))
{
// new status is not valid
}
if (newStatus == Status.IN_PROGRESS)
{
// new status in progress
}
}
}
I define Enum with underscore and replace when using later in code:
// Enum definition
public enum GroupName
{
Major_Features, Special_Features, Graphical_Features
};
// Use in code:
internal Group GetGroup(GroupName groupName)
{
//...
string name = groupName.ToString().Replace('_',' '));
//...
}
What is your purpose of this question, if you want to have a set of strings, then you want to have a integer value for each of keys, the best way is using a Dictionary from your keys and values, like this:
Dictionary<string, int> MyDictionary = new Dictionary<string, int>()
{
{"good Boy", 1 },
{"Bad Boy", 2 },
};
Then you can give the integer value from the key like this:
int value = MyDictionary["good Boy"];
You can write the spaced word in brackets. Then define a constructor to take up the values inside the bracket. As given below
public enum category
{
goodBoy("Good Boy"),
BadBoy("Bad Boy")
}
private String categoryType;
category(String categoryType) {
this.categoryType = categoryType;
}
You cannot have enum with spaces in .Net. This was possible with earlier versions of VB and C++ of course, but not any longer. I remember that in VB6 I used to enclose them in square brackets, but not in C#.
Since the original question was asking for adding a space within the enum value/name, I would say that the underscore character should be replaced with a space, not an empty string. However, the best solution is the one using annotations.
An enumerator cannot contain white space in its name.
As we know that enum is keyword used to declare enumeration.
you can check throw this link
https://msdn.microsoft.com/en-us/library/sbbt4032.aspx

Categories

Resources