Typesafe enum or attribute enum? - c#

My first approach looked like it would work very nicely until I got a runtime error I had no idea how to solve at var name = ((LCData)attributes[0]).Name; about index out of range. Practically I was just copying the code I found at Getting attributes of Enum's value so I was not 100% sure what it actually did. So when the following code didn't work, I moved on to another solution.
public enum Identification : ushort
{
[LCAttribute("IMG_BG01_Greens")]
BG01_Greens = 0,
[LCAttribute("Rabbit", "IMG_E01_Rabbit")]
ENEMY_E01_Rabbit = 2000,
}
public static class Enums
{
public static LCData GetInfo(Identification id)
{
var type = typeof(Identification);
var memInfo = type.GetMember(id.ToString());
var attributes = memInfo[0].GetCustomAttributes(typeof(LCData), false);
var name = ((LCData)attributes[0]).Name;
var tex = ((LCData)attributes[0]).Texture;
LCData data;
data.Name = name;
data.Texture = tex;
return data;
}
}
public struct LCData
{
public string Name;
public string Texture;
public LCData(Identification id)
{
this = Enums.GetInfo(id);
}
}
public class LCAttribute : System.Attribute
{
private string _Name;
public string Name
{
get
{
return _Name;
}
}
private string _Texture;
public string Texture
{
get
{
return _Texture;
}
}
public LCAttribute(string texture)
{
_Texture = texture;
}
public LCAttribute(string name, string texture)
{
_Name = name;
_Texture = texture;
}
}
Secondly I tried the typesafe enum approach. This had 2 fatal weaknesses I couldn't find a solution for:
1) I cannot get a list of available enum entries for looping operations.
2) I cannot get the corresponding enum entry by an id number.
public sealed class Identification
{
private readonly ushort _ID;
private readonly string _Name;
private readonly string _Tex;
public static readonly Identification BG01_Greens = new Identification(0, "IMG_BG01_Greens");
public static readonly Identification ENEMY_E01_Rabbit = new Identification(2000, "Rabbit", "IMG_E01_Rabbit");
private Identification(ushort id, string tex)
{
_ID = id;
_Tex = tex;
}
private Identification(ushort id, string name, string tex)
{
_ID = id;
_Name = name;
_Tex = tex;
}
public ushort ID { get { return _ID; } }
public string Name { get { return _Name; } }
public string Texture { get { return _Tex; } }
}
How should I proceed? Why doesn't my first solution work?

You are confusing LCData and LCAttriubte. Because LCAttribute is a valid attribute, but you are trying to use LCData as the attribute. (By the way, probably you don't need two separate types... but I bear with you).
This the ammended code:
public enum Identification : ushort
{
[LCAttribute("IMG_BG01_Greens")] //Look the type of the attributes is LCAttribute
BG01_Greens = 0,
[LCAttribute("Rabbit", "IMG_E01_Rabbit")]
ENEMY_E01_Rabbit = 2000,
}
public static class Enums
{
public static LCData GetInfo(Identification id)
{
var type = typeof(Identification);
var memInfo = type.GetMember(id.ToString());
//this will return an array of LCAttributes
var attributes = memInfo[0].GetCustomAttributes(typeof(LCAttribute), false);
//I tell you they are LCAttribute not LCData
var name = ((LCAttribute)attributes[0]).Name;
var tex = ((LCAttribute)attributes[0]).Texture;
//If the above were an LCData why would create a new one here? [Rethorical]
LCData data;
data.Name = name;
data.Texture = tex;
return data;
}
}
Note: For altenatives approaches and maybe some insight into this approach you can see my answer to How do you make an 'enum' that has data tied to it?. The approach you use here is listed under "Custom Attributes".

Related

Enum with parameters

Is it possible in C# to collect information in an enum instead of a dedicated class?
Example how it would work in Java:
public enum Action {
JUMP( "JUMP", 1),
CROUCH ("CROUCH", 2),
;
private String animationId;
private int buttonId;
private Action( String animationId, int buttonId) {
this.animationId = animationId;
this.buttonId = buttonId;
}
public String getAnimationId() {
return animationId;
}
public int getButtonId() {
return buttonId;
}
}
You can use enum with attributes:
public enum Action{
[MyValue("JUMP", 1)]
JUMP,
[MyValue("CROUCH", 2)]
CROUCH
}
[AttributeUsage(
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]
public class MyValueAttribute : System.Attribute{
public string Value{get; private set}
public string AnimationId{get; private set;}
public MyValueAttribute(string animationId, string value){
AnimationId = animationId;
Value = value;
}
and you can get value as follows:
public static class EnumExtensions{
public static string GetValue(this Enum value)
{
var type = value.GetType();
var name = Enum.GetName(type, value);
if (name == null) return string.Empty;
var field = type.GetField(name);
if (field == null) return string.Empty;
var attr = Attribute.GetCustomAttribute(field, typeof(MyValueAttribute)) as MyValueAttribute;
return attr != null ? attr.Value: string.Empty;
}
public static string GetAnimationId(this Enum value)
{
var type = value.GetType();
var name = Enum.GetName(type, value);
if (name == null) return string.Empty;
var field = type.GetField(name);
if (field == null) return string.Empty;
var attr = Attribute.GetCustomAttribute(field, typeof(MyValueAttribute)) as MyValueAttribute;
return attr != null ? attr.AnimationId: string.Empty;
}
}
Usage:
Action.JUMP.GetValue();
Action.JUMP.GetAnimationId();
or you can use one method which return for example Tuple with AnimationId and Value
No, but you can use static class fields instead:
public sealed class Action
{
public static readonly Action JUMP = new Action("JUMP", 1);
public static readonly Action CROUCH = new Action("CROUCH", 2);
public string AnimationId { get; }
public int ButtonId { get; }
private Action(String animationId, int buttonId)
{
AnimationId = animationId;
ButtonId = buttonId;
}
public override string ToString() => AnimationId;
}
You could definitely use attributes like suggested. However, you can call .ToString() on an enum value to get its name as a string value, and you can also assign int values to them. By default they are assigned 0-X based on index. However you could do this
public enum Action {
JUMP=1,
CROUCH=2
}
And then to access these values
Action action = Action.JUMP;
int value = (int) action; //Is set to 1
string name = action.ToString(); //Is set to "JUMP"
While this certainly will not work in every case depending on how much you want your enum to store, for the situation you described this is much easier.

Using Enumeration Class

I'm new to C# and I'm relatively new to abstract classes and inheritance and I'm having troubles understanding how to use them. I have this abstract enumeration class:
public abstract class Enumeration : IComparable
{
public uint Id { get; private set; }
public string Name { get; private set; }
public uint MaxTemperature { get; private set; }
public double Density { get; private set; }
protected Enumeration()
{
}
protected Enumeration(uint id, string name, uint maxTemprature, double density)
{
Id = id;
Name = name;
MaxTemperature = maxTemprature;
Density = density;
}
public static IEnumerable<T> GetAll<T>() where T : Enumeration,
new()
{
var type = typeof(T);
var fields = type.GetTypeInfo().GetFields(BindingFlags.Public
| BindingFlags.Static | BindingFlags.DeclaredOnly);
foreach (var info in fields)
{
var instance = new T();
var locatedValue = info.GetValue(instance) as T;
if (locatedValue != null)
{
yield return locatedValue;
}
}
}
public override bool Equals(object obj)
{
var otherValue = obj as Enumeration;
if (otherValue == null)
{
return false;
}
var typeMatches = GetType().Equals(obj.GetType());
var valueMatches = Id.Equals(otherValue.Id);
return typeMatches && valueMatches;
}
public int CompareTo(object other)
{
return Id.CompareTo(((Enumeration)other).Id);
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
}
This class is inherited by my material class:
class Material : Enumeration
{
public static readonly Material FreeSpace =
new Material(0, "Free Space", 0, 0);
public static readonly Material CarbonSteel =
new Material(1, "Carbon Steel", 2500, 0.284);
private Material()
{
}
private Material(uint id, string name, uint maxTemperature,
double density) : base(id, name, maxTemperature, density)
{
}
private static IEnumerable<Material> List()
{
return new[] { FreeSpace, CarbonSteel };
}
}
Now I want to use these materials in my part class:
class Part
{
private Material partMaterial;
public Part() { }
public Material PartMaterial
{
set
{
partMaterial = value;
}
}
}
This is where I'm stuck, how do I set a variable as one of the enumerated static objects so I can get the properties from those?
You can use SelectedItem instead of SelectedIndex
part.PartMaterial = (Material) MaterialCombo.SelectedItem;
So, I wish I would have left the question the way it was, because in the end it was the correct way to ask the question. But after the snide comments and down grading I changed it to what I thought was better. The way the original question should have been answered was:
Since you are enumerating the materials class, you need a method to expose the Enumerated values of the objects. The
IEnumerable<Material> List() method should be made public to accomplish this.
You can then use MaterialCombo.DataSource = Material.List() to populate the combobox with the material objects and MaterialCombo.DisplayMember = "Name"; to display the names of those objects in the combobox.
Finally, use #Oxald's answer to pass the material to your part class.
Thank you #Mark Benningfield for pointing me in the direction to search for "Using an enum to populate a combobox" which was helpful.
And Oxald for suggesting to use .SelectedItem instead of .SelectedIndex.

c# how to add a reference for a struct to an array or list?

New to c# and object oriented
I am creating predefined data held in structs (that will not change).
and I want to add a reference to those structs in either an array or a list
The thought process is to then loop through an array of these structs and if the filter criteria is met, to then add an element of the struct "Displayname" to a listbox
Example I am using here is for an array
public struct ListboxData
{
public string Category;
public string Displayname;
public string ID;
public List<string> IDTags;
public string FaultTxt;
public string Suggestion;
public string Comments;
public List<string> MoreQuestions;
}
public ListboxData COOLING = new ListboxData
{
Category = "CATEGORY",
Displayname = "COOLING",
ID = "CAT_COOL",
IDTags = { ""},
FaultTxt = "",
Suggestion = "",
Comments = "",
MoreQuestions = { ""}
};
Having now created multiple instances of the ListboxData structs I then want to add a selection of some of these structs to an array. In this case the Struct reference COOLING
Example of what I am trying to achieve
public ListboxData[] ARRAYofCATEGORIES =
{
COOLING
};
How can I achieve this ?
I re-wrote your example, working. You can find a live test here
Below is the code. I used a List() since you mentioned in your question that it was acceptable, and I am more used to it.
I don't know what you were doing wrong since you didn't show us a full example. You can read the code bellow, tell me if there is something you don't understand/want and i'll update my answer to fit your needs :-)
Here is the code
public struct ListboxData
{
private readonly string _category;
private readonly string _displayname;
private readonly string _id;
private readonly List<string> _idTags;
private readonly string _faultTxt;
private readonly string _suggestion;
private readonly string _comments;
private readonly List<string> _moreQuestions;
public ListboxData(string category, string displayname, string id, List<string> idTags, string faultTxt, string suggestion, string comments, List<string> moreQuestions)
{
_category = category;
_displayname = displayname;
_id = id;
_idTags = idTags;
_faultTxt = faultTxt;
_suggestion = suggestion;
_comments = comments;
_moreQuestions = moreQuestions;
}
public string Category
{
get { return _category; }
}
public string Displayname
{
get { return _displayname; }
}
public string Id
{
get { return _id; }
}
public List<string> IdTags
{
get { return _idTags; }
}
public string FaultTxt
{
get { return _faultTxt; }
}
public string Suggestion
{
get { return _suggestion; }
}
public string Comments
{
get { return _comments; }
}
public List<string> MoreQuestions
{
get { return _moreQuestions; }
}
}
public class Test
{
public List<ListboxData> Categories = new List<ListboxData>();
public CoolingListTest()
{
Categories.Add(new ListboxData(
category: "CATEGORY",
displayname: "COOLING",
id: "CAT_COOL",
idTags: new List<String> { "" },
faultTxt: "",
suggestion: "",
comments: "",
moreQuestions: new List<String> { "" }
));
}
}
public class Program
{
public static void Main(string[] args)
{
var test = new Test();
Console.WriteLine(test.Categories.First().Displayname);
}
}
1st rule to go by is that you have to design immutability into struct. Especially if the fields are reference types (string, array, user classes).
I will demonstrate below a simplified example of populating a struct with various values, then creating a collection (array or list) of the struct and then finally slicing one of the values as an array.
See live code here this code:
public struct ListboxData
{
public ListboxData(
int id,
string category,
string name,
params string[] tags) : this()
{
this.ID = id;
this.Category = category;
this.Name = name;
this.Tags = new List<string>();
this.Tags.AddRange(tags);
}
public int ID { get; private set; }
public string Category { get; private set;}
public string Name { get; private set;}
public List<string> Tags { get; private set;}
public string[] ToStringArray()
{
return new[] {
ID.ToString(),
Category,
Name,
string.Join(";", Tags)
};
}
}
public class Program
{
public static void Main(string[] args)
{
//Your code goes here
Console.WriteLine("Hello, world!");
var list = new List<ListboxData>();
list.Add(new ListboxData(1001, "COOLING", "Cooling Intro"));
list.Add(new ListboxData(1002, "COOLING", "Cooling Adanced"));
list.Add(new ListboxData(1003, "COOLING", "Cooling Tests"));
list.Add(new ListboxData(1004, "HEATING", "Heating Intro", "tag1", "tag2"));
// Get all cooling ID's
int[] ids = list.Where((item)=> item.Category=="COOLING").Select( (item)=> item.ID).ToArray();
// { 1001, 1002, 1003 }
foreach(var item in ids)
{
Console.WriteLine(item);
}
// Get all items that contain "Intro" in the name
ListboxData[] intro = list.Where((item)=>item.Name.Contains("Intro")).ToArray();
// { list[0], list[3] }
foreach(var item in intro)
{
Console.WriteLine(item.Name);
}
}
}
Newer versions of c# have simplifications for the above code. You can omit the private set; statements in the properties.

extend enum with attribute in other assembly

There exists an enum in an assembly:
public enum TheEnumeration
{
TheFirstValue = 1,
TheSecondValue = 2
}
In another Assembly I would like to extend this enumeration with some Attributes (I know this is not valid code, just to show the idea):
public enum MyExtendedEnumeration : TheEnumeration
{
[MyAttribute("The First Value")]
TheFirstValue,
[MyAttribute("The 2nd Value")]
TheSecondValue
}
Is there a way to achieve this goal in a proper way?
You can't Extend enums, you can't inherit from them. You may just have to create a new Enum that repeats the values like a pass through and then decorate yours.
public enum MyExtendedEnumeration
{
[MyAttribute("The First Value")]
TheFirstValue = TheEnumeration.TheFirstValue,
[MyAttribute("The 2nd Value")]
TheSecondValue = TheEnumeration.TheFirstValue
}
See: Extending enums in c#
Enums cant inherit from another Enum. They are based on the System.Enum
You can put Attributes on the members.
Creating a class/Type that behaves somewhat like an Enum, may be a useful in scenarios like this.
Assumes you can "alter" the orginal enum.
///
/// Sample of a STRING or non int based enum concept.
///
public sealed class FilterOp {
private static readonly Dictionary<string, FilterOp> EnumDictionary = new Dictionary<string, FilterOp>();
private readonly string _name;
private readonly string _value;
public const string Eq = "Eq";
public const string Ne = "Ne";
public const string Gt = "Gt";
public const string Ge = "Ge";
public const string Lt = "Lt";
public const string Le = "Le";
public const string And = "And";
public const string Or = "Or";
public const string Not = "Not";
public static readonly FilterOp OpEq = new FilterOp(Eq);
public static readonly FilterOp OpNe = new FilterOp(Ne);
public static readonly FilterOp OpGt = new FilterOp(Gt);
public static readonly FilterOp OpGe = new FilterOp(Ge);
public static readonly FilterOp OpLt = new FilterOp(Lt);
public static readonly FilterOp OpLe = new FilterOp(Le);
public static readonly FilterOp OpAnd = new FilterOp(And);
public static readonly FilterOp OpOr = new FilterOp(Or);
public static readonly FilterOp OpNot = new FilterOp(Not);
private FilterOp(string name) {
// extend to cater for Name / value pair, where name and value are different
this._name = name;
this._value = name;
EnumDictionary[this._value] = this;
}
public override string ToString() {
return this._name;
}
public string Name {
get { return _name; }
}
public string Value {
get { return _value; }
}
public static explicit operator FilterOp(string str) {
FilterOp result;
if (EnumDictionary.TryGetValue(str, out result)) {
return result;
}
return null;
}
}

with enum retrieve const value from class c#

i have an enum like
public enum DecimailPrecision
{
One,
Two,
}
and class as
class DecimailPrecision1
{
public const string One = "#,##0.0";
public const string Two = "#,##0.00";
}
i want to retrieve const string from class with enum. i already doing this with if and switch as
string format = string.Empty;
switch (value)
{
case DecimailPrecision.One:
format = DecimailPrecision1.One.ToString(); break;
case DecimailPrecision.Two:
format = DecimailPrecision1.Two.ToString(); break;
default:
format = DecimailPrecision1.Two.ToString(); break;
}
if (value == "One"){
format = DecimailPrecision1.One.ToString();}
else if (value == "Two"){
format = DecimailPrecision1.Two.ToString();}
}
i need a better way because i have lot items in enum.
thanks.
Why not just create a Dictionary<DecimailPrecision, string> and hold the mappings in that?
That way you can simply look up your DecimailPrecision value in the dictionary and retrieve the appropriately mapped string.
You could even store the mapping in config and read it from that, so you wouldn't need to recompile your code to add new mappings.
To explicitly apply this to your code (I'd recommend changing the name of your consts to DecimalPrecisionFormat):
var precisionMap = new Dictionary<DecimailPrecision, string>
{
{ DecimailPrecision.One, DecimailPrecision1.One }
, { DecimailPrecision.Two, DecimailPrecision1.Two }
};
var formatTwo = precisionMap[DecimailPrecision.Two];
For similar needs we developed a custom attribute and a few extension methods.
Usage is like this.
public enum DecimailPrecision
{
[EnumCode("#,##0.0")]
One,
[EnumCode("#,##0.00")]
Two
}
string format = DecimailPrecision.One.GetCode();
It may be meaningless for your case but reverse is valid through like this
string format ="#,##0.00";
DecimailPrecision dp = format.ToEnum<DecimailPrecision>();
Extensions and Atrribute are like:
public static class EnumExtensions
{
private static readonly Dictionary<Type, EnumCodePair[]> EnumCodeCache = new Dictionary<Type, EnumCodePair[]>();
public static string GetCode(this Enum enumValue) {
var codePairs = GetEnumCodePairs(enumValue.GetType());
return codePairs.First(cp => Equals(cp.Enum, enumValue)).Code;
}
public static T ToEnum<T>(this string enumCode) {
var codePairs = GetEnumCodePairs(typeof(T));
return (T)codePairs.First(cp => Equals(cp.Code, enumCode)).Enum;
}
private static IEnumerable<EnumCodePair> GetEnumCodePairs(Type type) {
if(!EnumCodeCache.ContainsKey(type)) {
var enumFields = type.GetFields(BindingFlags.Public | BindingFlags.Static);
var codePairs = new List<EnumCodePair>();
foreach(var enumField in enumFields) {
var enumValue = Enum.Parse(type, enumField.Name);
var codePair = new EnumCodePair {
Enum = enumValue
};
var attrs = enumField.GetCustomAttributes(typeof(EnumCodeAttribute), false);
codePair.Code = attrs.Length == 0
? enumField.Name
: ((EnumCodeAttribute)attrs[0]).Code;
codePairs.Add(codePair);
}
EnumCodeCache.Add(type, codePairs.ToArray());
}
return EnumCodeCache[type];
}
class EnumCodePair
{
public object Enum { get; set; }
public string Code { get; set; }
}
}
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public class EnumCodeAttribute : Attribute
{
public EnumCodeAttribute(string code) {
Code = code;
}
public string Code { get; private set; }
}

Categories

Resources