C# "Enum" Serialization - Deserialization to Static Instance - c#

Suppose you have the following class:
class Test : ISerializable {
public static Test Instance1 = new Test {
Value1 = "Hello"
,Value2 = 86
};
public static Test Instance2 = new Test {
Value1 = "World"
,Value2 = 26
};
public String Value1 { get; private set; }
public int Value2 { get; private set; }
public void GetObjectData(SerializationInfo info, StreamingContext context) {
//Serialize an indicator of which instance we are - Currently
//I am using the FieldInfo for the static reference.
}
}
I was wondering if it is possible / elegant to deserialize to the static instances of the class?
Since the deserialization routines (I'm using BinaryFormatter, though I'd imagine others would be similar) look for a constructor with the same argument list as GetObjectData(), it seems like this can't be done directly . . Which I would presume means that the most elegant solution would be to actually use an enum, and then provide some sort of translation mechanism for turning an enum value into an instance reference. However, I personally like that the "Enum"'s choices are directly linked with their data.
How might one go about this?

If you need more data with with the Enums, consider using attributes. Example below.
class Name : Attribute
{
public string Text;
public Name(string text)
{
this.Text = text;
}
}
class Description : Attribute
{
public string Text;
public Description(string text)
{
this.Text = text;
}
}
public enum DaysOfWeek
{
[Name("FirstDayOfWeek")]
[Description("This is the first day of 7 days")]
Sunday = 1,
[Name("SecondDayOfWeek")]
[Description("This is the second day of 7 days")]
Monday= 2,
[Name("FirstDayOfWeek")]
[Description("This is the Third day of 7 days")]
Tuesday= 3,
}
Perhaps this will allow you to provide more information with the Enums. You can access the attributes through reflection. If you need an example to retrieve the attribute I can provide that as well but I'm trying to keep this somewhat short.

Use Enum.Parse...Suppose you have the following:
Enum myEnum{
Foo = 1,
Bar = 2,
Baz = 3
};
Then
myEnum myE = myEnum.Foo; /* Default! */
myE = (myEnum)Enum.Parse(myE.GetType(), "Baz");
/* Now, myE should be Baz! */
Console.WriteLine("Enum Selected: {0}", myE.ToString());
The above sample serves to illustrate how to convert a string literal into an enum. I hope this is what you are looking for.

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)

Create a custom DisplayAttribute for enums?

I've got an enum class...
public enum LeadStatus : byte
{
[Display(Name = "Created")] Created = 1,
[Display(Name = "Assigned")] Assigned = 2,
....
}
Name of course is out-of-the-box. From MetaData...
namespace System.ComponentModel.DataAnnotations
{
public sealed class DisplayAttribute : Attribute
{
...
public string Name { get; set; }
...
}
}
Suppose I wanted my own custom Display Attribution, such as "BackgroundColor"...
[Display(Name = "Created", BackgroundColor="green")] Created = 1
I've seen a few other threads here that kinda dance around the issue, but the context is different enough that I can't make it work. I assume I need to create some sort of extension / override class, but I am not picturing this in my head.
Thanks!
Having your own attribute.
public sealed class ExtrasDisplayAttribute : Attribute
{
public string Name { get; set; }
public string BackgroundColor { get; set; }
}
And this extension method.
namespace ExtensionsNamespace
{
public static class Extensions
{
public static TAttribute GetAttribute<TAttribute>(Enum value) where TAttribute : Attribute
{
return value.GetType()
.GetMember(value.ToString())[0]
.GetCustomAttribute<TAttribute>();
}
}
}
Now you can extract attribute from enum like this.
using static ExtensionsNamespace.Extensions;
//...
var info = GetAttribute<ExtrasDisplayAttribute>(LeadStatus.Created);
var name = info.Name;
var bg = info.BackgroundColor;
//...
public enum LeadStatus : byte
{
[ExtrasDisplay(Name = "Created", BackgroundColor = "Red")] Created = 1,
[ExtrasDisplay(Name = "Assigned")] Assigned = 2,
}
If you want to still use the original attribute you can have that too.
you should apply both attributes to single enum.
public enum LeadStatus : byte
{
[Display(Name = "Created"), ExtrasDisplay(BackgroundColor = "Red")]Created = 1,
[Display(Name = "Assigned")] Assigned = 2,
}
And extract each one you want.
var name = GetAttribute<DisplayAttribute>(LeadStatus.Created).Name;
var bg = GetAttribute<ExtrasDisplayAttribute>(LeadStatus.Created).BackgroundColor;
public sealed class DisplayAttribute : Attribute is a sealed class and therefore you cannot inherit it and add other behavior or properties to it.
Below is my assumption but someone can chime in if they know why
And you may wonder why .NET developers made it sealed? I am wondering the same and my assumption is because each of the properties in DisplayAttribute are used to inject javascript, html etc. If they left it open, and you added a BackgroundColor property to it, what does that mean? What would that do in the UI?
having concluded this this isn't possible, I went with another kind of solution. Not as tidy as I had hoped for originally, but it still gets the job done.
Methods inside enum in C#

Enum Display Name - Use variable?

Can I do this ? It doesn't seem so.
public enum Options
{
[Display(Name = string.Format("{0} - {1}","Option One", MyClass.myVariable))]
OptionOne=1,
[Display(Name = string.Format("{0} - {1}","Option Two", MyClass.myVariable))]
OptionTwo=2
}
As opposed to this
public enum Options
{
[Display(Name = "Option 1")]
OptionOne=1,
[Display(Name = "Option 2")]
OptionTwo=2
}
If not, how can I make the Display Name for an enum variable ?
Seems like nobody's dealing with:
If not, how can I make the Display Name for an enum variable ?
I can think about some kind of enum map plus extension method which could work like this:
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
public enum Foo
{
One = 1,
Two = 2,
}
public static class ExtensionMethods
{
private static readonly Dictionary<Enum, string> s_EnumMap = new Dictionary<Enum, string>
{
{ Foo.One, string.Format("{0} - {1}","Option One", 1) },
{ Foo.Two, string.Format("{0} - {1}","Option Two", 2) }
};
public static String ConvertToString(this Enum eff)
{
return s_EnumMap[eff];
}
}
internal class Program
{
private static void Main(string[] args)
{
Console.WriteLine(Foo.One.ConvertToString());
Console.WriteLine(Foo.Two.ConvertToString());
}
}
}
Integers 1 and 2 can be of course replaced by e.g. static variable, such as MyClass.myVariable. If that is the way you would use this code, then keep in mind that s_EnumMap will store the values that MyClass.myVariable variable had at the time when you first used ExtensionMethods class (i.e. when static fields of MyClass were getting initialized). So modifying the code like this:
public MyClass
{
public static int myVariable = 5;
}
public static class ExtensionMethods
{
private static readonly Dictionary<Enum, string> s_EnumMap = new Dictionary<Enum, string>
{
{ Foo.One, string.Format("{0} - {1}","Option One", MyClass.myVariable) },
{ Foo.Two, string.Format("{0} - {1}","Option Two", 2) }
};
public static String ConvertToString(this Enum eff)
{
return s_EnumMap[eff];
}
}
internal class Program
{
private static void Main(string[] args)
{
Console.WriteLine(Foo.One.ConvertToString());
Console.WriteLine(Foo.Two.ConvertToString());
MyClass.myVariable = 100;
Console.WriteLine(Foo.One.ConvertToString());
Console.WriteLine(Foo.Two.ConvertToString());
}
}
Would result into:
Option One - 5
Option Two - 2
Option One - 5
Option Two - 2
While after commenting out the first two Console.WriteLines, the output would be:
Option One - 100
Option Two - 2
So if you want to dynamicaly react to changes of MyClass.myVariable then you have to implement some logic to update s_EnumMap`, but as long as I don't know more about the goal you are trying to achieve I cannot provide a better answer.
You could write a separate method to get the display name, or even a small class that has an option member and a display name member. I like Michal's idea better, but since I already started writing this I figured I'd throw it out there!
public enum Option
{
OptionOne = 1,
OptionTwo = 2
}
public static string GetOptionDisplayName(Option option)
{
switch (option)
{
case Option.OptionOne:
return string.Format("{0} - {1}", "Option One", MyClass.MyProperty);
case Option.OptionTwo:
return string.Format("{0} - {1}", "Option Two", MyClass.MyProperty);
default:
return option.ToString();
}
}
public class AnotherOption
{
public Option Option { get; set; }
public string DisplayName
{
get { return GetOptionDisplayName(this.Option); }
}
}
What you want cannot be done. The compiler needs to know the value at compile time.
The short answer no. The value within [Display....] can be known at compile time only. E.g. you can define literals, like string or enum value. string.Format() is called at run time. If possible you should call it in you other logic, using you enum. Or use code generating, e.g. with a tt template

Automapper - Mapping Entity to Enum

I'm trying to map an entity to a enum. As I was searching for a source I found this:
using Should;
public enum OrderStatus : short
{
InProgress = 0,
Complete = 1
}
public enum OrderStatusDto
{
InProgress = 0,
Complete = 1
}
[Test]
public void Example()
{
Mapper.Map<OrderStatus, OrderStatusDto>(OrderStatus.InProgress)
.ShouldEqual(OrderStatusDto.InProgress);
Mapper.Map<OrderStatus, short>(OrderStatus.Complete).ShouldEqual((short)1);
Mapper.Map<OrderStatus, string>(OrderStatus.Complete).ShouldEqual("Complete");
Mapper.Map<short, OrderStatus>(1).ShouldEqual(OrderStatus.Complete);
Mapper.Map<string, OrderStatus>("Complete").ShouldEqual(OrderStatus.Complete);
}
but I think this works for only enum-to-enum mapping. because when I try to use .ShouldEqual, intellisense can't find it. In that codeblock, there is a reference that's called Should but I couldn't find its reference anywhere.
Any ideas about how to use automapper to map between enum and entity/class?
Any ideas about using Should?
#I updated the question because without seeing the actual code, it's harder to consider a solution. Here is the code snippet that might be needed:
public class ParameterEnum
{
/// <summary>
/// Enum Sayisi: 2650, Son Guncelleme Tarihi: 21.2.2013 09:40:37
/// </summary>
public enum Parameters : int
{
...
IsEmriTuruIsTalebi = 138,
<summary>
Adi: Kalite Öneri; ID: 2218; Seviyesi: 3; Aciklamasi: ; Aktif Mi: True
</summary>
...}}
and this is where normal mapping done:
isEmriEntity.IsEmriTuruId = (int)ParameterEnum.Parameters.IsEmriTuruIsTalebi;
You should look into ITypeConverter. Something like this should do the job:
Mapper.CreateMap<OrderStatus, OrderStatusDto>().ConvertUsing(new OrderStatusConverter());
and your converter would look like so:
public class OrderStatusConverter: ITypeConverter<OrderStatus, OrderStatusDto>
{
public OrderStatusDto Convert(OrderStatus source)
{
return (OrderStatusDto)source;
}
}
That should be enough to apply the same approach to any other cross-type mappings in your DTOs.
EDIT:
On your enum conversion error, using this as an example for clarity (an enum is not a DTO):
public enum ExampleEnum : short
{
SomeValue,
SomeOtherValue,
BigValue = 100,
}
public enum AnotherEnum
{
Foo,
Bar,
}
This should make the enum conversion clearer (don't cast to int at all).
private void Test()
{
// Casting to int only works when the value is 0
// This works (SomeValue = 0)
AnotherEnum example = (int) ExampleEnum.SomeValue;
// This won't even compile (SomeOtherValue = 1)
AnotherEnum example2 = (int) ExampleEnum.SomeOtherValue;
// Casting to another enum works fine
AnotherEnum example2 = (AnotherEnum) ExampleEnum.SomeOtherValue;
// Just be careful of values that don't exist in the target enum
// This will compile even though it won't work at run-time (BigValue = 100)
AnotherEnum example2 = (AnotherEnum) ExampleEnum.BigValue;
}

Adding list into system attribute

I have created custom attribute that is part of MEF where I would like to define list of ids that are relevant to the class so I can query on them.
Also the class has to contain definition within itself, this is important that is why i thought about using:
[SignalSystemData("ServiceIds", new List<int>(){1})]
How shall I proceed?
My implementation of search is as follows:
var c = new Class1();
var v = c.EditorSystemList;
foreach (var lazy in v.Where(x=>x.Metadata.LongName=="ServiceIds"))
{
if (lazy.Metadata.ServiceId.Contains(serviceIdToCall))
{
var v2 = lazy.Value;
// v2 is the instance of MyEditorSystem
Console.WriteLine(serviceIdToCall.ToString() + " found");
}else
{
Console.WriteLine(serviceIdToCall.ToString() + " not found");
}
}
My Export class with definition should look like this:
[Export(typeof(IEditorSystem))]
[SignalSystemData("ServiceIds", new List<int>{1})]
public class MyEditorSystem1 : IEditorSystem
{
void test()
{
Console.WriteLine("ServiceID : 1");
}
}
public interface IEditorSystem
{
}
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class SignalSystemDataAttribute : ExportAttribute
{
public SignalSystemDataAttribute(string longName, List<int> serviceId)
: base(typeof (IEditorSystem))
{
LongName = longName;
ServiceId = serviceId;
}
public string LongName { get; set; }
public List<int> ServiceId { get; set; }
}
public interface IEditorSystemMetadata
{
string LongName { get; }
List<int> ServiceId { get; }
}
To get around the compile time constant issue, you have the following choices:
Use a specially formatted string (i.e. a comma separated list of integers, as you already suggested).
Use a number of overloads, each with a different number of arguments for the IDs. This will get messy if you have too many IDs to be passed.
Use params int[] ids as the last argument to your constructor. This will work, but is not CLS compliant - if that matters for you.
Most easily use an array int [] argument.
Of course you could also use a combination of the above. Having a couple of overloads with say 1 to 5 ID arguments and providing a string argument or params int[] argument for those (hopefully) corner cases, where the overload arguments are not sufficient.
Update: Just found this question/answer. Might not be duplicate as such, but deals with the same issue (mostly).

Categories