How to get Enum object by value in C#? - c#

I recently encountered a case when I needed to get an Enum object by value (to be saved via EF CodeFirst), and here is my Enum:
public enum ShipmentStatus {
New = 0,
Shipped = 1,
Canceled = 2
}
So I needed to get ShipmentStatus.Shipped object by value 1.
So how could I accomplish that?

This should work, either (just casting the int value to enum type):
int _val = 1;
ShipmentStatus _item = (ShipmentStatus)_val;
Beware, that it may cause an error if that enum is not defined.

Why not use this build in feature?
ShipmentStatus shipped = (ShipmentStatus)System.Enum.GetValues(typeof(ShipmentStatus)).GetValue(1);

After some battling with Enum I created this - a universal helper class that will do what I needed - getting key by value, and more importantly - from ANY Enum type:
public static class EnumHelpers {
public static T GetEnumObjectByValue<T>(int valueId) {
return (T) Enum.ToObject(typeof (T), valueId);
}
}
So, to get Enum object ShipmentStatus.Shipped this will return this object:
var enumObject = EnumHelpers.GetEnumObjectByValue<ShipmentStatus>(1);
So basicaly you can use any Enum object and get its key by value:
var enumObject = EnumHelpers.GetEnumObjectByValue<YOUR_ENUM_TYPE>(VALUE);

Related

Implementing an Enum Array to String Array TypeConverter in .NET

In C# or VB.NET, under WinForms, I have a property that returns an array of a enum. See an example:
public enum TestEnum: int {
Name1 = 0,
Name2 = 1,
Name3 = 2
} // Note that the enum does not apply for the [Flags] attribute.
public TestEnum[] TestProperty {get; set;} =
new[] {TestEnum.Name1, TestEnum.Name2, TestEnum.Name3};
By default, a PropertyGrid will show the values as int[], like: {0, 1, 2} instead of the enumeration value names, like: {"Name1", "Name2", "Name2"}, which is the visual representation that I would like to acchieve...
So, I would like to design a TypeConverter that could be able to return a string array with the value names, and apply it like this:
[TypeConverter(typeof(EnumArrayToStringArrayTypeConverter))]
public TestEnum[] TestProperty {get; set;} =
new[] {TestEnum.Name1, TestEnum.Name2, TestEnum.Name3};
In other words, If my property is represented like this in a PropertyGrid:
I would like to have this:
The biggest problem I'm facing is trying to retrieve the type of the enum from the custom type-converter class, to be able get the value names of that enum. I only can get the primitive data type of the array (like: int[], uint16[], etc)...
public class EnumArrayToStringArrayTypeConverter : TypeConverter {
// ...
public override object ConvertTo(ITypeDescriptorContext context,
CultureInfo culture,
object value,
Type destinationType) {
if (destinationType == null) {
throw new ArgumentNullException(nameof(destinationType));
}
try {
// This will return the array-type for the
// primitive data type of the declared enum,
// such as int[], uint16[], etc.
Type t = value.GetType();
// I'm stuck at this point.
// ...
} catch (Exception ex) {
}
return null;
}
// ...
}
Please take into account that I'm asking for a reusable solution that can work for any kind of enum. And, my enum in this example does not have the [Flags] attribute applied, but a solution should care about enums having it, so, If a enum item of the enum array is a enum that has various flags, those flags (the value names) should be concatenated for example using string.join().
The PropertyGrid does already show the names for the enum values. It can even handle [Flags] correctly. See the sample below using a form with a default PropertyGrid and a default button and nothing else.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
[Flags]
public enum TestEnum : int
{
Name1 = 0,
Name2 = 1,
Name3 = 2
}
public class TestObject
{
public string Name { get; set; } = "Hello World";
public TestEnum[] TestProperty { get; set; } =
new[] { TestEnum.Name1, TestEnum.Name2 | TestEnum.Name3, TestEnum.Name3 };
}
private void button1_Click(object sender, EventArgs e)
{
TestObject o = new TestObject();
propertyGrid1.SelectedObject = o;
}
}
Please supply some example code that can reproduce that the enum names are not shown in the PropertyGrid. You must be doing something wrong in the first place.
As mentioned by #NineBerry in his answer, the PropertyGrid does already show the names for the enum values. However, I discovered that exists a strange circumstance on wich it will not do it...
Since my original source-code is written in VB.NET, I'll put VB.NET sample codes to reproduce the issue.
The thing is that I was getting a value from a instance of a WMI class (specifically: Win32_DiskDrive.Capabilities), which returns an object that needs to be casted to a uint16 array. Then, I was casting that resulting uint16 array to my type of enum. To simplify things, I will not show WMI code, but an object that represents what I was getting from WMI...
Dim wmiValue As Object = {1US, 2US, 3US}
Dim castedValue As UShort() = DirectCast(wmiValue, UShort())
TestProperty = DirectCast(castedValue, TestEnum())
So, when doing that type-casting, and thanks to #NineBerry answer, I discovered that for some reason the default type converter of TestProperty goes wrong and the PropertyGrid shows uint16 values instead of the enum value-names.
( Note that using DirectCast() or CType() in VB.NET, it didn't changed the PropertyGrid behavior. )
To fix the error, I ended using Array.ConvertAll(), and then the PropertyGrid properly shows the value-names...
Dim wmiValue As Object = {1US, 2US, 3US}
Dim castedValue As UShort() = DirectCast(wmiValue, UShort())
TestProperty = Array.ConvertAll(castedValue,
Function(value As UShort)
Return DirectCast(value, TestEnum)
End Function)

Enums casting to a byte

Am using EntityFramework and have a LinkStatusID column which is a tinyint, which gets generated into a byte in C#.
public enum LinkStatus
{
Added = 0,
Deleted = 1
}
however this gives:
a.LinkStatusID = (byte)Enums.LinkStatus.Deleted;
is there a more elegant way to structure this?
EDIT2 for LastCoder:
public enum LinkStatus : byte
{
Added = 0,
Deleted = 1
}
var blah = Enums.LinkStatus.Added;
var ty = blah.GetType();
var blah2 = (byte)Enums.LinkStatus.Added;
var ty2 = blah2.GetType();
This doesn't work (as I expected) however the first answer here explains why.
EDIT3:
EF isn't the only way this sln gets to the DB, so I'm keen to keep the Enums explicit in the code. Thanks for EF5 Enum suggestions!
public enum LinkStatus : byte
Will avoid the explicit cast.
You should probably use the native enum support, like SLaks mentioned above (Tutorial is here). If you don't want to do that, you can do something else that I found for before EF supported it:
public int CountryInt{get;set;}
public Countries Country
{
get { return (Countries) this.CountryInt; }
set { this.CountryInt = (int) value; }
}
Using this, then, allows you to just set the Country variable, and have it automatically go to the DB as the correct int value.
A static class can be used as a workaround
public static class LinkStatus
{
public static byte
Added = 0,
Deleted = 1;
}

Converting a string to a custom enum

I'm trying to set a custom enum property on a custom object by looking at a string value that is held in another object, but I keep getting the error "cannot reference a type through an expression."
so far I've tried
rec.Course = (CourseEnum)Enum.Parse(typeof(CourseEnum), rr.course);
where rec.Course wants a member of the CourseEnum Enumeration, and rr.course is a string.
I also tried to do a switch statement where the value of rr.course is checked (there are only certain values it can be) but get the same result
the enum is defined as follows:
public enum CourseEnum
{
[StringValue("Starters")]
Starters,
[StringValue("Main Course")]
MainCourse,
[StringValue("Desserts")]
Desserts
};
public class StringValue : System.Attribute
{
private string _value;
public StringValue(string value)
{
_value = value;
}
public string Value
{
get { return _value; }
}
}
public static class StringEnum
{
public static string GetStringValue(Enum value)
{
string output = null;
Type type = value.GetType();
//Check first in our cached results...
//Look for our 'StringValueAttribute'
//in the field's custom attributes
FieldInfo fi = type.GetRuntimeField(value.ToString());
StringValue[] attrs =
fi.GetCustomAttributes(typeof(StringValue),
false) as StringValue[];
if (attrs.Length > 0)
{
output = attrs[0].Value;
}
return output;
}
}
I can see in your code that your using Enum.Parse with CourseEnum and it should be recipeCourse I presume.
I can't spot any place in your sample code where4 CourseEnum is defined.
A #Hans Kesting said, the answer was here: Why can not reference a type through an expression?
The problem was using a field that has an enum type with the enum type itself.

Set one enum equal to another

I have 2 enums in 2 different objects. I want to set the enum in object #1 equal to the enum in object #2.
Here are my objects:
namespace MVC1 {
public enum MyEnum {
firstName,
lastName
}
public class Obj1{
public MyEnum enum1;
}
}
namespace MVC2 {
public enum MyEnum {
firstName,
lastName
}
public class Obj2{
public MyEnum enum1;
}
}
I want to do this, but this wont compile:
MVC1.Obj1 obj1 = new MVC1.Obj1();
MVC2.Obj2 obj2 = new MVC2.Obj2();
obj1.enum1 = obj2.enum1; //I know this won't work.
How do I set the enum in Obj1 equal to the enum in Obj2? Thanks
Assuming that you keep them the same, you can cast to/from int:
obj1.enum1 = (MVC1.MyEnum)((int)obj2.enum1);
Enums have an underlying integer type, which is int (System.Int32) by default, but you can explicitly specify it too, by using "enum MyEnum : type".
Because you're working in two different namespaces, the Enum types are essentially different, but because their underlying type is the same, you can just cast them:
obj1.enum1 = (MVC1.MyEnum) obj2.enum1;
A note: In C# you have to use parentheses for function calls, even when there aren't any parameters. You should add them to the constructor calls.
Best way to do it is check if it's in range using Enum.IsDefined:
int one = (int)obj2.enum1;
if (Enum.IsDefined(typeof(MVC1.MyEnum), one )) {
obj1.enum1 = (MVC1.MyEnum)one;
}
obj1.enum1 = (MVC1.MyEnum) Enum.Parse(typeof(MVC1.MyEnum),
((int)obj2.enum1).ToString());
or
int one = (int)obj2.enum1;
obj1.enum1 = (MVC1.MyEnum)one;

What's the return type of an anonymous class

I have a class that used to have a string return type. Now I find I need to return more than a string. I was thinking to return something like below:
public string Test()
{
return ( new { ID = 5, Name= "Dave" } );
}
Is this even possible and if so then what would be the return type? I know it's not string ..
As others have said, the best thing to do here is to make a nominal type. I would suggest that the nominal type have the same characteristics as an anonymous type; that is, you should consider making the type immutable and consider making it exhibit value equality.
It is possible to return an anonymous type as object and then use the instance returned elsewhere using a variety of sneaky techniques. You can cast the object to "dynamic" (in C# 4) and then use the properties of the anonymous type, but this is slow and lacks compile-time type checking.
You can also use the "cast by example" trick, which does get you compile-time type checking. However, that trick only works when the anonymous source object and the anonymous example object come from the same assembly.
static T CastByExample<T>(object source, T example) where T : class
{
return source as T;
}
static object ReturnsAnonymous() { return new { X = 123 }; }
static void DoIt()
{
object obj = ReturnsAnonymous();
var example = new { X = 0 };
var anon = CastByExample(obj, example);
Console.WriteLine(anon.X); // 123
}
See how sneaky that is? We use method type inference and local variable type inference to tell the compiler "these two things are the same type". This lets you export an anonymous type as object and cast it back to anonymous type.
But you probably should not do this; if you're resorting to such sneaky tricks then you should simply be defining a nominal type in the first place. Also, like I said, the trick only works if the example and the source objects were created in code in the same assembly; two "identical" anonymous types in two different assemblies do not unify to be the same type.
The object that you return does have a class, but it's anonymous so you can't specify it in the code. You just have to return it as an object reference:
public object Test() {
return new { ID = 5, Name= "Dave" };
}
Note that the anonymous type is unknown outside the scope of the method, so reflection is the only way to access its properties.
If you want to be able to use the returned object conveniently, you should declare a class:
public class TestResult
{
public int ID { get; set; }
public string Name { get; set; }
}
public TestResult Test() {
return new TestResult() { ID = 5, Name= "Dave" };
}
Another alternative is to use an existing class, if it fits your purpose. A KeyValuePair is close to what you use, but then the properties will of course be named Key and Value instead of ID and Name:
public KeyValuePair<int, string> Test() {
return new KeyValuePair<int, string>(5, "Dave");
}
This isn't possible as the anonymous class is only valid within the current context. If you need to return an object then you'll need to create a real class.
I'm assuming you left string as the return type by accident.
Anonymous type are class type that are derived directly from object.
You can return it from method as object as return type.
Have a look at this.
No, it's not possible. Your options are:
Define a real class for the return value,
Use System.Tuple, or
Use out parameters (probably the least good option).
You can make a struct (or class) for this.
public struct IdAndName
{
public int Id;
public string Name;
public IdAndName(int id, string name)
{
ID = id;
Name = name;
}
}
You could also use a Tuple<T1, T2>, (but that's not recommended as the properties aren't named.
class NewString
{
public int ID { get; set; }
public string Name { get; set; }
}
public NewString Test()
{
return ( new NewString() { ID = 5, Name = "Dave" } );
}
:)

Categories