How to decorate enum with attribute in c# - c#

I have an enum on helper library in my solution.
For example
public enum MyEnum
{
First,
Second
}
I want to use MyEnum in a few another project. I want to decorate this enum in each project with own attribute like this:
public enum MyEnum
{
[MyAttribute(param)]
First,
[MyAttribute(param2)]
Second
}
How to decorate enum from another library with own local attribute?

You can't do what you've described - the best you can do is to create a new Enum that uses the same set of values. You will then need to cast to the "real" enum whenever you use it.
You could use T4 templates or similar to generate the attributed enum for you - it would be much safer that way as it would be very easy to map the wrong values, making for some very subtle bugs!
Linqpad Query
enum PrimaryColor
{
Red,
Blue,
Green
}
enum AttributedPrimaryColor
{
[MyAttribute]
Red = PrimaryColor.Red,
[MyAttribute]
Blue = PrimaryColor.Blue,
[MyAttribute]
Green = PrimaryColor.Green
}
static void PrintColor(PrimaryColor color)
{
Console.WriteLine(color);
}
void Main()
{
// We have to perform a cast to PrimaryColor here.
// As they both have the same base type (int in this case)
// this cast will be fine.
PrintColor((PrimaryColor)AttributedPrimaryColor.Red);
}

Attributes are compile-time additions (metadata) to code. You can not modify them when using the compiled code assembly.
(Or perhaps you could if you are a diehard low-level IL wizard, but I certainly am not...)
If your enum values require modification or parameters at various places, then you should consider other solutions, e.g. a Dictionary or even a Database Table.
E.g. using a Dictionary:
var values = new Dictionary<MyEnum, int>()
{
{ MyEnum.First, 25 },
{ MyEnum.Second, 42 }
};
var valueForSecond = values[MyEnum.Second]; // returns 42

You can do something like this, but it will be tedious.
The idea is to use your project settings to allow the change when you import the enum in a new project.
First, you will need 2 attributes:
// This one is to indicate the format of the keys in your settings
public class EnumAttribute : Attribute
{
public EnumAttribute(string key)
{
Key = key;
}
public string Key { get; }
}
// This one is to give an id to your enum field
[AttributeUsage(AttributeTargets.Field)]
public class EnumValueAttribute : Attribute
{
public EnumValueAttribute(int id)
{
Id = id;
}
public int Id { get; }
}
Then, this method:
// This method will get your attribute value from your enum value
public object GetEnumAttributeValue<TEnum>(TEnum value)
{
var enumAttribute = (EnumAttribute)typeof(TEnum)
.GetCustomAttributes(typeof(EnumAttribute), false)
.First();
var valueAttribute = (EnumValueAttribute)typeof(TEnum).GetMember(value.ToString())
.First()
.GetCustomAttributes(typeof(EnumValueAttribute), false)
.First();
return Settings.Default[String.Format(enumAttribute.Key, valueAttribute.Id)];
}
I did not check if the type is correct, not even if it finds any attributes. You will have to do it, otherwise you will get an exception if you don't provide the right type.
Now, your enum will look like that:
[Enum("Key{0}")]
public enum MyEnum
{
[EnumValue(0)] First,
[EnumValue(1)] Second
}
Finally, in your project settings, you will have to add as many lines as the number of members in your enum.
You will have to name each line with the same pattern as the parameter given to EnumAttribute. Here, it's "Key{0}", so:
Key0: Your first value
Key1: Your second value
etc...
Like this, you only have to change your settings values (NOT THE KEY) to import your enum and change your attributes to a project to another.
Usage:
/*Wherever you put your method*/.GetEnumAttributeValue(MyEnum.First);
It will return you "Your first value".

Related

Exclude specific Enum values from Swagger

I always add an Uninitialized value to all my enums and set it to 0 to handle cases where I deserialize an object that has an enum property value that was never set.
enum MyEnum
{
Uninitialized = 0,
MyEnumValue1 = 1,
MyEnumValue2 = 2,
MyEnumValue3 = 3,
}
However, I don't want the Uninitialized value to show up in my Swagger documentation.
I've tried adding the [JsonIgnore] attribute to that value, but that didn't work.
Anyone know how to accomplish this?
Just in case anyone else struggles with this. You can create a custom SchemaFilter and populate the Enum property filtering on those enum values with a custom attribute (in this example: OpenApiIgnoreEnumAttribute).
public class OpenApiIgnoreEnumSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
if (context.Type.IsEnum)
{
var enumOpenApiStrings = new List<IOpenApiAny>();
foreach (var enumValue in Enum.GetValues(context.Type))
{
var member = context.Type.GetMember(enumValue.ToString())[0];
if (!member.GetCustomAttributes<OpenApiIgnoreEnumAttribute>().Any())
{
enumOpenApiStrings.Add(new OpenApiString(enumValue.ToString()));
}
}
schema.Enum = enumOpenApiStrings;
}
}
}
public class OpenApiIgnoreEnumAttribute : Attribute
{
}
public enum ApplicationRole
{
[OpenApiIgnoreEnum]
DoNotExpose = 1,
ValueA = 2,
ValueB = 3,
}
You can use an IDocumentFilter to remove anything you want from the specs.
It might not be intuitive at first, look at some of the examples they have:
https://github.com/domaindrivendev/Swashbuckle.AspNetCore/search?q=IDocumentFilter
With that you are able to change the swagger json spec to remove or inject anything you want.
Now be careful you could end up with a non-compliant spec, always check it against the validator: https://validator.swagger.io/validator/debug?url=http://swagger-net-test.azurewebsites.net/swagger/docs/V1
You can simply omit your Uninitialized enum value to solve this.
Enums can actually contain values other than the ones you explicitly define. I can do var myEnumValue = (MyEnum)12345; and it won't break or throw an exception, but it won't match any of the explicitly defined enum values either.
As long as the defined values do not equal default(int), or the default of whatever you chose your enum type to be, you can still work with the enum as expected, and catch unitialized values with a switch default case.
This has the added benefit of catching all unlisted enum values, not just the one you explicitly declared.
Royston46's answer was excellent and helped build a foundation, but I had to make a couple of modifications to get it to fully work for my scenario.
The first issue was handling nullable types. If the underlying property associated with the enum is nullable, context.Type.IsEnum will return false.
The second issue is that finding the member by value to examine the custom attributes won't work correctly when you have multiple enums with the same value. In our case, we deprecated some old enum names, but left them in the enum for compatibility with existing API consumers. However, we needed the documentation to only show the new names.
Here is the revised implementation that we built from Royston46's excellent answer:
public class OpenApiIgnoreEnumSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
if (context.Type.IsEnum || (Nullable.GetUnderlyingType(context.Type)?.IsEnum ?? false))
{
var type = context.Type.IsEnum ? context.Type : Nullable.GetUnderlyingType(context.Type);
var enumOpenApiStrings = new List<IOpenApiAny>();
foreach (var enumName in Enum.GetNames(type))
{
var member = type.GetMember(enumName)[0];
if (!member.GetCustomAttributes<OpenApiIgnoreEnumAttribute>().Any())
{
enumOpenApiStrings.Add(new OpenApiString(enumName));
}
}
schema.Enum = enumOpenApiStrings;
}
}
}

C# Dash (-) in String Enum

I'm creating an enum for a field in a Class and I need one of the values to be Start-up. However, when I type this, I get an error } expected. StartUp, on the other hand, is allowed but I really need the dash to be in there..
I also have other fields with the same problem for spaces..
Does anyone know of a way that I can get the value as shown above?
Code Example:
using System;
namespace XMLConverter.Models
{
public enum SiteStatus { Hold, Planned, Proposed, Qualifying, StartUp, Open, Enrollment Complete, Closed, Stopped, Not Selected, Withdrew }
public class Site
{
public string StudyName { get; set; }
public string SiteNumber { get; set; }
public string SiteName { get; set; }
public SiteStatus SiteStatus { get; set; }
}
}
You can't. Identifiers in C# don't allow dashes since it is an operator, so that is the same for enum values.
There is no way around this. I would suggest to replace that for another sign, like an underscore (_). Depending on the use of the enum, you might have luck with your serializer. If you for example use JSON, there are possibilities to serialize and deserialize a value differently.
Enum members are just like every other identifier, and as such their names need to satisfy a set of rules. Part of these rules means that you cannot have dashes in identifier or enum names names.
And if you think about it practically, it does not make a lot of sense. Just imagine what would happen if Start and up are two variables. Since C# ignores whitespace in expressions, Start-up would mean Start - up which is a subtraction operation.
While the enum member itself needs to be a valid identifier, you can control how it is rendered to other parties. For example, you can use the DisplayAttribute to affect how it is rendered on UIs, and you can the DataMemberAttribute to affect how it is serialized.
An identifier in c# cannot contain the dash character, so you can't do this.
If you need to obtain a description for an enum value, you could use the [Description] attribute, For example:
enum TestEnum
{
[Description("Start-up")]
StartUp
}
TestEnum val = TestEnum.StartUp;
string desc = GetEnumDescription((TestEnum)val);
Where the method GetEnumDescription looks like this: (source: How to get C# Enum description from value?)
public static string GetEnumDescription(Enum value)
{
FieldInfo fi = value.GetType().GetField(value.ToString());
DescriptionAttribute[] attributes =
(DescriptionAttribute[])fi.GetCustomAttributes(
typeof(DescriptionAttribute),
false);
if (attributes != null &&
attributes.Length > 0)
return attributes[0].Description;
else
return value.ToString();
}
There is no reason to be putting "-" in the enum values.
1.If you want to display it that way to your applications users you can create an attribute and check the value of the attribute when displaying the enum value.
2.as #patric pointed out replace it with a character and check the existense of the character when displaying the value and replace it with '-'
At the first look, it's weird, but nowadays in microservices, some other languages (like GoLang) use 'dash' in the enum name, and it is okay for them, but in c# we have to add an attribute for them to handle this problem.
using System;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
public class Program
{
public static void Main()
{
var toolViewMode = ToolViewModeType.Extended;
Console.WriteLine(toolViewMode.GetEnumMemberValue());
}
}
public static class Extensions {
public static string GetEnumMemberValue<T>(this T value)
where T : struct, IConvertible
{
return typeof(T)
.GetTypeInfo()
.DeclaredMembers
.SingleOrDefault(x => x.Name == value.ToString())
?.GetCustomAttribute<EnumMemberAttribute>(false)
?.Value;
}
}
public enum ToolViewModeType
{
[EnumMember(Value = "basic-view")]
Basic,
[EnumMember(Value = "extended-view")]
Extended
}

C# How to use get, set and use enums in a class

I have a program where I use a class store settings. I need it to use set and get functions to change and store settings. I have tried this, and I don't get it to work. Can anyone help me with this one?
private enum _Difficulty { Easy, Normal, Hard };
public void SetDifficulty(Difficulty)
{
_Difficulty = Difficulty;
}
public enum GetDifficulty()
{
return _Difficulty;
}
Is there no way to use enums in a class with get and set?
I also need this with bool and int.
There are several things wrong here:
Your enum is private, but your methods are public. Therefore you can't make your methods return type be the enum type, or have parameters with that type
Your SetDifficulty method has a parameter of just Difficulty - is that meant to be the parameter name or the type?
Your SetDifficulty method is trying to set the type rather than a field
Your GetDifficulty method is trying to use enum as a return type, and is then returning a type rather than a field
Basically, you seem to be confused about what your enum declaration is declaring - it's not declaring a field, it's declaring a type (and specifying what the named values of that type are).
I suspect you want:
// Try not to use nested types unless there's a clear benefit.
public enum Difficulty { Easy, Normal, Hard }
public class Foo
{
// Declares a property of *type* Difficulty, and with a *name* of Difficulty
public Difficulty Difficulty { get; set; }
}
You can use get/set methods if you really want to make your code look like Java instead of C#:
public enum Difficulty { Easy, Normal, Hard }
public class Foo
{
private Difficulty difficulty;
public void SetDifficulty(Difficulty value)
{
difficulty = value;
}
public Difficulty GetDifficulty()
{
return difficulty;
}
}
Once you specify an enum using the enum keyword, that enum acts as a type, like a class or struct would.
Here's how you'd implement a property with a custom enum:
public enum _Difficulty { Easy, Normal, Hard };
public _Difficulty Difficulty { get; set; }
You code tries to assign Difficulty a value, when in fact Difficulty is the name of the enum type. I would encourage use of getters and setters as properties instead:
public enum Difficulty { Easy, Normal, Hard };
private Difficulty _difficulty;
public Difficulty CurrentDifficulty
{
get { return _difficulty; }
set { _difficulty = value; }
}
This way you can add additional code in the setter for special conditions. To use it you simply do the following:
//set
CurrentDifficulty = Difficulty.Easy;
//get
Difficulty theDifficulty = CurrentDifficulty;
The enum you want to use needs to be public. You also need an instance of that enum to store the value you're setting, you're currently setting it to your enum's declaration. Then you want to use the same enum type for what your class stores and what you pass in.
public enum Difficulty { Easy, Normal, Hard };
public Difficulty { get; set; }
There are a number of issues here:
Your enum is private, so nothing will be able to call SetDifficulty to provide it with a value from that enum. Indeed, the compiler won't allow this.
The argument to SetDifficulty is just a type, it also needs a variable name. Also a compiler error.
You're trying to get/set the value of the enumeration itself, rather than a class-level variable of the type of the enumeration. This too won't compile.
It looks like you want to do this:
public enum Difficulty { Easy, Normal, Hard }
public Difficulty DifficultySetting { get; set; }
Note that I had to change the name of the property to DifficultySetting because it conflicts with the enum name. Yours used an underscore, which would also solve that problem. However I always found that underscores are for private members and if you want consuming code to use this enum then it would need to be public. Decorating a public member with things like underscores distracts from the semantics of the name itself. In any event, the semantics are up to your personal preference, so you can modify these names as you see fit.
I'm not sure you are using them correctly. This might help...
private enum Difficulty { Easy, Normal, Hard };
private Diffuculty theDifficulty;
public void SetDifficulty(difficulty d)
{
theDifficulty = difficulty;
}
public Difficulty GetDifficulty()
{
return theDifficulty;
}
An enum is a type not a variable. Something like this would work:
public enum Difficulty { Easy, Normal, Hard };
private Difficulty _Difficulty;
public void SetDifficulty(Difficulty difficulty )
{
_Difficulty = Difficulty;
}
public Difficulty GetDifficulty()
{
return _Difficulty;
}
or more succinctly as an Auto Property:
private Difficulty Difficulty {get; set;}
Note that the enum has to be public if you want it to be accessible from public methods.

Are custom attributes for Enums dangerous?

I am building an application that makes heavy use of Enums for custom data. Essentially, an object is stored in the database with about 28 separate attributes. Each attribute is a two-character field that's translated from the SQL straight over to an Enum.
Unfortunately, I need to also translate these values into two different human-readable values. One for a legend on a data table, and one for a CSS class to style an image on the web application front-end.
To do this, I've set up two custom attributes and applied them to the Enum where necessary. For example:
Custom Attribute Interface
public interface IAttribute<T>
{
T Value { get; }
}
Example Custom Attribute
public sealed class AbbreviationAttribute: Attribute, IAttribute<string>
{
private readonly string value;
public AbbreviationAttribute(string value)
{
this.value = value;
}
public string Value
{
get { return this.value; }
}
}
Method to Retrieve Custom Attribute from Enum
public static R GetAttributeValue<T, R>(IConvertible #enum)
{
R attributeValue = default(R);
if (#enum != null)
{
FieldInfo fi = #enum.GetType().GetField(#enum.ToString());
if (fi != null)
{
T[] attributes = fi.GetCustomAttributes(typeof(T), false) as T[];
if (attributes != null && attributes.Length > 0)
{
IAttribute<R> attribute = attributes[0] as IAttribute<R>;
if (attribute != null)
{
attributeValue = attribute.Value;
}
}
}
}
return attributeValue;
}
Example Enum Using This Pattern
public enum Download
{
[Abbreviation("check")]
[Description("Certified")]
C = 1,
[Abbreviation("no-formal")]
[Description("No formal certification")]
NF = 2,
[Abbreviation("cert-prob")]
[Description("Certified with potential problems")]
CP = 3
}
Both Abbreviation and Description are custom attributes that implement IAttribute<T>. My actual Enum has 11 possible values, and as I mentioned before it's used in 28 separate properties in my custom object. Using custom attributes seemed the best way to map this information back and forth.
Now for the question, is this the best way to accomplish this? I store the Enum value ("C", "NF", or "CP" in the snippet above) in the database, but I need the values of the Abbreviation and Description in my code. Also, I doubt this will be the final set of custom attributes I'll need.
Before I keep moving forward with this pattern ... is it the right way to do things? I'd rather fix potential problems with this approach now than have to track back and refactor later.
This is the same method I use. The one downside is serialization. The custom attributes values do not serialize.
I like the custom attribute method over the database method because it ties the attribute data right to the enum instead of having to use a lookup table or class, etc.
I'd probably build a hash table and a special type for something like this. You may have already discarded the idea for some reason or another, but here's what I would do not knowing the specifics of your application.
class SpecialType {
// include the fields and all attributes that you need to reference, ToString method for debugging, and any serialization you need
public string foo { get; set; }
public string bar { get; set; }
public ToString() { return "SpecialType with foo '" + foo + "' and bar '" + bar + "'"; }
}
Dictionary<int, SpecialType> myDict = new Dictionary<int, SpecialType> {
{ 1, new SpecialType { foo = "XA1B2", bar = "XC3D4" } },
{ 2, new SpecialType { foo = "ZA1B2", bar = "ZC3D4" } },
{ 3, new SpecialType { foo = "YA1B2", bar = "YC3D4" } },
}
Then I could easily keep ints in my other classes to save memory, find out if a particular value was valid by checking for existence in the Keys of the Dictionary, all that jazz. It would probably be a lot easier to do databinding if you're eventually going to use WPF or read/write to disk, too.
Can you alter the database? I think the best option would be to make a table (or tables) to house the possible values of the enums and foreign key the main objects over to it (instead of using char codes - this makes pulling it in easier and normalizes your DB). Give the table an Abbreviation and Description column, then pull those in and reference them by their key, and cache them if lookups are slow.
One thing that's dangerous about the attributes is that if any of those strings ever have to change, it's a complete redeploy of the app. If you make them database values, you can change them with a simple UPDATE.

Enum of GUID's/Guid interop

I'm doing some interop and need to pass some GUID's. Is there a way to add a GUID attribute to an enum value and when it is marshalled it has the appropriate value?
Basically I'm trying to convert the code
#if !defined( STATIC_KSDATAFORMAT_SUBTYPE_PCM )
#define STATIC_KSDATAFORMAT_SUBTYPE_PCM\
DEFINE_WAVEFORMATEX_GUID(WAVE_FORMAT_PCM)
DEFINE_GUIDSTRUCT("00000001-0000-0010-8000-00aa00389b71", KSDATAFORMAT_SUBTYPE_PCM);
#define KSDATAFORMAT_SUBTYPE_PCM DEFINE_GUIDNAMED(KSDATAFORMAT_SUBTYPE_PCM)
#endif
(and several other similar ones)
and use them in an enum so I can specify the appropriate subtype format easily. Obviously I could probably just use a dictionary or some other similar method but I would like to make it as transparent as possible.
It would be nice to do something like
enum MyGuids : Guid
{
Guid1 = GUID("...") or just "..."
}
I can use a class/struct instead
static class MyGuids
{
public static Guid flag1 = new Guid("9ED54F84-A89D-4fcd-A854-44251E925F09");
}
But the only problem here is there is no way to relate the Guid in unmanaged structure to this class. It's specified as Guid and if I replaced it with MyGuids then it won't be a Guid any more. e.g., I lose type safety since any guid can end up in the field and not just the ones from MyGuid.
Any ideas?
Guids are structures in .NET, they are too large to fit in a simple value type by a factor of two. Sounds to me that you need a structure member initialized. That's going to need an assignment statement in your code. The const will work just fine for this. There is otherwise no way to get the compiler to do it automatically.
var sound = new foo();
sound.waveFormat = MyGuids.flag1;
// etc..
How about something like this?
void Main()
{
var dog = MyValues.Dog.ToId();
var cat = MyValues.Cat.ToId();
var bird = MyValues.Bird.ToId();
}
public enum MyValues
{
Dog,
Cat,
Bird
}
public static class Functions
{
public static Guid ToId(this MyValues value)
{
switch (value)
{
case MyValues.Dog:
return Guid.Parse("6d139d6a-2bfa-466d-a9a5-c6e82f9abf51");
case MyValues.Cat:
return Guid.Parse("AA139d6a-2bfa-466d-a9a5-c6e82f9abf51");
case MyValues.Bird:
return Guid.Parse("BB139d6a-2bfa-466d-a9a5-c6e82f9abf51");
default:
throw new InvalidDataException();
}
}
}

Categories