Setting enum value at runtime in C# - c#

Is there any way that I can change enum values at run-time?
e.g I have following type
enum MyType
{
TypeOne, //=5 at runtime
TypeTwo //=3 at runtime
}
I want at runtime set 5 to TypeOne and 3 to TypeTwo.

As others have pointed out, the answer is no.
You could however probably refactor your code to use a class instead:
public sealed class MyType
{
public int TypeOne { get; set; }
public int TypeTwo { get; set; }
}
...
var myType = new MyType { TypeOne = 5, TypeTwo = 3 };
or variations on that theme.

Just refer to MSDN help HERE
An enumeration type (also named an enumeration or an enum) provides an efficient way to define a set of named integral constants that may be assigned to a variable.
Also HERE
In the Robust Programming Section - Just as with any constant, all references to the individual values of an enum are converted to numeric literals at compile time.
So you need to realign your idea of Enum and use it accordingly.
To answer your question - No it is not possible.

Enums are compiled as constant static fields, their values are compiled into you assembly, so no, it's not possible to change them. (Their constant values may even be compiled into places where you reference them.)
Eg take this enum:
enum foo
{
Value = 3
}
Then you can get the field and its information like this:
var field = typeof(foo).GetField("Value", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
Console.WriteLine(field.GetValue(null));
Console.WriteLine(field.Attributes);

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;
}
}
}

Can i keep instances of class inside enum?

Everywhere i meet enums with string as a value and int as an index, but what if i want to keep instances inside enum like this:
public enum TestEnum
{
new Person("John"),
new Person("Jack")
}
Is it possible?
Just asking
//Edit 2019: I was so stupid then, should have used as a array
This can't be done.
An enum type is a distinct value type (§4.1) that declares a set of named constants.
The body of an enum type declaration defines zero or more enum members, which are the named constants of the enum type. No two enum members can have the same name.
Each enum member has an associated constant value. The type of this value is the underlying type for the containing enum. The constant value for each enum member must be in the range of the underlying type for the enum.
Enums have members that are named constants and an associated constant value. Each Enum also has an underlying type, which defaults to int. The constant values can be given explicitly, or they can be implicit.
enum Color
{
Red,
Blue,
Green
}
is equivalent to
enum Color : int
{
Red = 0,
Blue = 1,
Green = 2
}
A class instance, such as new Person("Jack"); is not a constant, and not valid as an enum member or value.
Beyond just the validity of trying to do this, I'm not certain how you would expect this to be used. Are you looking to be able to access a TestEnum.Jack and retrieve your Person("Jack") instance? If so, you could accomplish something similar to this with a class that has static readonly properties with the correct defaults provided.
public static class People {
public static Person Jack {get;} = new Person("Jack");
public static Person John {get;} = new Person("John");
}
You can now get your person instances with People.John and People.Jack.
This can lead into issues if the Person class is mutable, despite these properties being readonly.

How to decorate enum with attribute in 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".

As per CA1008 rule of FXCop Enums should have a default value of zero. Is this applicable for C#?

Should this rule be applicable to C#?
The compiler gives error "Use of unassigned local variable" if we try to use the enum without explicitly setting a value?
The perspective being asked is the validity of the FxCop rule since I cannot use the default value of an enum.
public enum TraceLevel
{
Off = 0,
Error = 1,
Warning = 2,
Info = 3,
Verbose = 4
}
class Program
{
static void Main(string[] args)
{
TraceLevel traceLevelOptions;
Console.WriteLine(traceLevelOptions.ToString());
Console.ReadLine();
}
}
Updated after getting the right answer. The following code should work:
public class SerializeMe
{
public int Id { get; set; }
public TraceLevel MyTrace { get; set; }
}
public enum TraceLevel
{
Off = 0,
Error = 1,
Warning = 2,
Info = 3,
Verbose = 4
}
class Program
{
static void Main(string[] args)
{
SerializeMe serializeMe = new SerializeMe();
Console.WriteLine(serializeMe.MyTrace.ToString());
Console.ReadLine();
}
}
The reason that enums should have a zero value is explained in the documentation for the Code Analysis error that relates to it:
http://msdn.microsoft.com/en-us/library/ms182149.aspx
CA1008: Enums should have zero value
The default value of an uninitialized enumeration, just like other value types, is zero. A non-flags−attributed enumeration should define a member that has the value of zero so that the default value is a valid value of the enumeration.
So the reason is that if, for example, you declare an enum field in a class or struct and do not initialise it, it will have the default value of zero. If there is no member of the enum with a zero value, you will in that (fairly common) situation have a enum field containing an invalid value.
There are also other instances where you can end up with a default-initialised enum field (e.g. during deserialization). You want to avoid a default-initialised enum field having an invalid value, hence the rule.
This isn't a FXCop error, it is a C# compiler error. In C# all the local variables must be initialized before being used.
TraceLevel traceLevelOptions = 0; // or TraceLevel.Error for example
Like for all the other types... If traceLevelOptions was an int, you would get the same error.
There is a loophole for struct types. You can assign them with a value or assign all their fields with a value, and they will be considered assigned. Note that this condition sometimes can't be satisfied if the struct has, for example, private fields.

How to create an array of enums

I have about 30 different flagged enums that I would like to put into an array for indexing and quick access. Let me also claify that I do not have 1 enum with 30 values but I have 30 enums with differing amounts of values.
The goal would be to add them to an array at a specifed index. This way I can write a functions in which I can pass the array index into for setting particuler values of the enum.
Updated:
Here is an example of what I am wanting to do.
enum main(
enum1 = 0,
enum2 = 1,
enumn = n-1 ) - this has indexs which would match the index of the associated enum
[flag]
enum1(value1=0, value2=1, value3=2, value4=4...)
[flag]
enum2("")
[flag]
enum2("")
since I am using flagable enums I have a class like the following
public static class CEnumWorker
{
public static enum1 myEnum1 = enum1.value1;
public static enum2 myEnum2 = enum2.value1;
public static enumN myEnumN = enumN.value1;
//I would then have functions that set the flags on the enums. I would like to access the enums through an array or other method so that I do not have to build a large switch statement to know which enum I am wanting to manipulate
}
Since you have 30 different types of enums, you can't create a strongly typed array for them. The best you could do would be an array of System.Enum:
Enum[] enums = new Enum[] { enum1.Value1, enum2.Value2, etc };
You would then have to cast when pulling an enum out of the array if you need the strongly typed enum value.
If I understand correctly, you would have to do:
object[] enums = new object[30];
enums[0] = Enum1.Value1;
enums[1] = Enum2.AnotherValue;
But then you would have to access like this (not strongly typed, and easy to reference the wrong index):
if ((Enum1)enums[0] == Enum1.Value1)
...
In .NET 4, you could use the Tuple:
var enums = new Tuple<Enum1, Enum2>(Enum1.Value1, Enum2.AnotherValue);
if (enums.Item1 == Enum1.Value1)
...
...but I wouldn't recommend it for so many (30) enums, and I think there's even a limit of 8 or so. You can also create your own class:
class Enums
{
public Enum1 Enum1 { get; set; }
public Enum2 Enum2 { get; set; }
}
Enums enums = new Enums();
enums.Enum1 = Enum1.Value1;
enums.Enum2 = Enum2.AnotherValue;
if (enums.Enum1 == Enum1.Value1)
...
I would recommend the last way because you're using a reference type, you're not dependent on index position, you can give the properties whatever name you want, and it's strongly typed. The only thing you "lose" is the ability to easily iterate through the list, but you can achieve that with Reflection if you really need to.
You could always use good old object[], but that means doing a lot of casting.
Enum provides two mechanisms for converting an integer to an enum value - the GetValues() method and plain casting:
enum EnumA { A1, A2, A1234 }
enum EnumB { B00, B01, B02, B04 }
class Program
{
static void Main(string[] args)
{
EnumA a = ((EnumA[])Enum.GetValues(typeof(EnumA)))[0];
Console.WriteLine(a);
EnumB boa = (EnumB)3;
Console.WriteLine(boa);
}
}
I'm not quite sure why you want to create your own arrays if you can use one of these mechanisms to get an enum from an int.

Categories