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;
}
}
Related
Is there a way to make the default Enum.ToString() to convert to snake_case instead of PascalCase? And that change to be global, so I don't have to do that all over again.
public enum SpellTypes
{
HorizonFocus
}
public sealed class Settings
{
public Settings(SpellTypes types)
{
TypeString = types.ToString(); // actual: HorizonFocus, expected: horizon_focus
}
public string TypeString { get; }
}
In addition
I tried the following with Macross.Json.Extensions but it didn't apply the changes to the TypeString.
[JsonConverter(typeof(JsonStringEnumMemberConverter))]
public enum SpellTypes
{
[EnumMember(Value = "horizon_focus")]
HorizonFocus
}
You could create an extension method for all enums.
You can cache the snake case names in a dictionary in a generic helper class, for performance.
public static string To_snake_case<T>(this T value) where T : Enum
{
return SnakeCaseHelper.Values.TryGetValue(value, out var name) ? name : null;
}
private static class SnakeCaseHelper<T> where T : Enum
{
public static Dictionary<T, string> Values = new();
static SnakeCaseHelper()
{
var names = Enum.GetNames(typeof(T));
var values = (T[])Enum.GetValues(typeof(T));
for (var i = 0; i < values.Length; i++)
Values[values[i]] = GetSnakeCase(names[1]);
}
static string GetSnakeCase(string text)
{
if(text.Length < 2)
return text;
var sb = new StringBuilder();
sb.Append(char.ToLowerInvariant(text[0]));
for(int i = 1; i < text.Length; ++i)
{
char c = text[i];
if(char.IsUpper(c))
{
sb.Append('_');
sb.Append(char.ToLowerInvariant(c));
}
else
{
sb.Append(c);
}
}
return sb.ToString();
}
}
dotnetfiddle
Snake case function is from this answer.
you can use readonly type instead of enum
public class SpellTypes
{
public static readonly SpellTypes HorizonFocus = new SpellTypes( 1, "Horizon_Focus" );
public static readonly SpellTypes HorizonFocus2 = new SpellTypes( 2, "Horizon_Focus2" );
public static readonly SpellTypes HorizonFocus3 = new SpellTypes( 3, "Horizon_Focus3" );
public readonly int num;
public readonly string name;
private SpellTypes( int num, String name )
{
this.num = num;
this.name = name;
}
}
public sealed class Settings
{
public Settings( SpellTypes types )
{
TypeString = types.name.ToString();
}
public string TypeString { get; }
}
I am currently building a namespace to handle complicated string actions. because I use the this string keyword, I must declare where the functions and properties are located as static. (the name of this class is "StringExtension") now I have another class named StringExtensionSettings and I use its boolean properties to determent what functions in the class StringExtension will be enabled. (for the user to choose what functions he wants to use and what not)
ex:
public class StringExtensionSettings
{
public bool DecryptString { get; set; } = true;
public bool EncryptString { get; set; } = true;
public bool RandomMix { get; set; } = true;
public bool AddMidSubString { get; set; } = true;
}
I don't want to warp the string in a class because it will make it complicated for the user. is there is any way to enable or disable function in a static class based on another class properties? and/or how to declare a class within a static class?
thank you in advance!
Additional resources:
the StringExtension class:
static class StringExtension
{
//this is what I'm trying to declare: gives an error
public StringExtensionSettings StringSettings = new StringExtensionSettings();
public static string AddMidSubString(this string Str, string MidSubString)
{
StringBuilder Result = new StringBuilder(Str);
Result.Insert(Result.Length / 2, MidSubString);
return Result.ToString();
}
public static string RandomMix(this string Str)
{
char[] array = Str.ToCharArray();
Random rng = new Random();
int n = array.Length;
while (n > 1)
{
n--;
int k = rng.Next(n + 1);
var value = array[k];
array[k] = array[n];
array[n] = value;
}
return new string(array);
}
// and more functions...
Follow-up of my comment in the OP
Within a Singleton (class), you are still able/ allowed to define fields.
The singleton design pattern is an interface. It is a popular class
type for programs. It allows a class to enforce that it is only
allocated (read -> created) once.
public sealed class StringExtensionSettings
{
private StringExtensionSettings()
{
}
private static StringExtensionSettings instance = null;
public static StringExtensionSettings Instance
{
get
{
if (instance == null)
{
instance = new StringExtensionSettings();
}
return instance;
}
}
public bool DecryptString { get; set; } = true;
public bool EncryptString { get; set; } = true;
public bool RandomMix { get; set; } = true;
public bool AddMidSubString { get; set; } = true;
}
Usage:
Single Field call
StringExtensionSettings.Instance.AddMidSubString
Implementation
public static string AddMidSubString(this string Str, string MidSubString)
{
if (StringExtensionSettings.Instance.AddMidSubString)
{
StringBuilder Result = new StringBuilder(Str);
Result.Insert(Result.Length / 2, MidSubString);
return Result.ToString();
}
throw new Exception($"Not allowed to call {nameof(AddMidSubString)}");
}
Summarized; calling StringExtensionSettings.Instancecreates a new instance of StringExtensionSettings, only (once!), when the private field instance of StringExtensionSettings is null.
I have no idea if this is possible but it feels like it should be from using C# up to this point.
I want to have a bunch of static classes that contain 'set values' for users of the library to send into another class as parameter.
So this is where I was headed but I can't figure it out. This below is just an example of what I was thinking so don't try and work out 'why' :-)
First - The Class that will be called
public class myClass
{
public bool isError { private set; get; }
public DataTable output { private set; get; }
public String filename { set; private get; }
public settingModule settings { set; private get; }
public static void execute()
{
//Call Private 'getTheData'
//set isError accordingly
//Load output
}
private static DataTable getTheData()
{
//Open and read file for 'fileName'
//Use settings.startingRow
//Use settings.fileType
//User settings.skipEmpty
//Do some stuff
return Datatable from workings
}
}
Second - The Class I want to user to pass
public static class settingMobule
{
public static class fileTypeA
{
public static int startingRow = 1;
public static String fileType = "txt";
public static bool skipEmpty = true;
}
public static class fileTypeB
{
public static int startingRow = 10;
public static String fileType = "csv";
public static bool skipEmpty = false;
}
public static class fileTypeC
{
public static int startingRow = 3;
public static String fileType = "hex";
public static bool skipEmpty = true;
}
}
Lastly the way I want to be able to call it
myClass test = new myClass();
test.filename = "c:\\temp\\test.txt;
test.settings = settingModule.fileTypeA;
test.execute();
if(test.isError == false
{
DataTable myTable = test.output;
test.dispose()
}
Thanks in advance... and yes, "your nuts there is a much better way" is a perfectly valid answer :-)
I would also LOVE to know how to add a .dispose() to my code, it's not something i have got to yet but while I am here... :-D
No, basically; but you could do this:
public sealed class SettingMobule
{
public int StartingRow {get; private set;}
public string FileType {get; private set;}
public bool SkipEmpty {get; private set;}
private SettingMobule(int startingRow, string fileType, bool skipEmpty)
{
StartingRow = startingRow;
FileType = fileType;
SkipEmpty = skipEmpty;
}
public static SettingMobule FileTypeA {get;}
= new SettingMobule(1, "txt", true);
public static SettingMobule FileTypeB {get;}
= new SettingMobule(10, "csv", false);
public static SettingMobule FileTypeC {get;}
= new SettingMobule(3, "hex", true);
}
and pass SettingMobule.FileTypeA as an instance, etc.
No. This is not possible. It is not possible because of 2 reasons:
Static classes cannot be passed around.
The receiver cannot know that these classes are supposed to contain the same set of settings and has no way to access them.
Choose another approach where there is only one non-static file type class used to create several setting objects: (C# 6.0)
public class FileType
{
public FileType(int startingRow, string extension, bool skipEmpty)
{
this.StartingRow = startingRow;
this.Extension = extension; // 'FileType': Member names cannot be the same as their
// enclosing type.
this.SkipEmpty = skipEmpty;
}
public int StartingRow { get; }
public string Extension { get; }
public bool SkipEmpty { get; }
}
The static settings class can now present several setting objects of the same type that can be passed around.
public static class SettingModule
{
public static FileType TxtFileType { get; } = new FileType(1, "txt", true);
public static FileType CsvFileType { get; } = new FileType(10, "csv", false);
public static FileType HexFileType { get; } = new FileType(3, "hex", true);
}
Now, the test class could be written as:
public class MyTestClass
{
private FileType fileType;
private string filename;
public MyTestClass(FileType fileType, string filename)
{
this.fileType = fileType;
this.filename = filename;
}
public void Execute()
{
Console.WriteLine(
$"Extension = {fileType.Extension}, starting row = {fileType.StartingRow}");
}
}
And you can perform the test like this
var test = new MyTestClass(SettingModule.TxtFileType, #"c:\temp\test.txt");
test.Execute();
Non-static classes are a kind of template from which numerous objects can be created. Unlike static classes, such classes are types that can be used to declare variables, method parameters, properties and more.
Unfortunately in C# static classes are extremely limited in what they will allow you to do.
However, with Reflection and Types, you can do something similar, but I don't think you should.
void Main() {
var test = new MyClass(typeof(settingModule.fileTypeB));
Console.WriteLine(test.StartingRow);
}
public class MyClass {
Type SettingsClass { get; set; }
public MyClass(Type sc) {
SettingsClass = sc;
}
public int StartingRow {
get {
return (int)SettingsClass.GetField("startingRow", BindingFlags.Static | BindingFlags.Public).GetValue(null);
}
}
}
public static class settingModule {
public static class fileTypeA {
public static int startingRow = 1;
public static String fileType = "txt";
public static bool skipEmpty = true;
}
public static class fileTypeB {
public static int startingRow = 10;
public static String fileType = "csv";
public static bool skipEmpty = false;
}
public static class fileTypeC {
public static int startingRow = 3;
public static String fileType = "hex";
public static bool skipEmpty = true;
}
}
I think what you should do is create instances of a subclass and pass that:
void Main() {
var test = new MyClass();
test.Settings = settingModule.fileTypeA;
Console.WriteLine(test.Settings.startingRow);
}
public class MyClass {
public settingModule.settingsSet Settings { get; set; }
}
public static class settingModule {
public class settingsSet {
public readonly int startingRow;
public readonly string fileType;
public readonly bool skipEmpty;
public settingsSet(int sr, string ft, bool se) {
startingRow = sr;
fileType = ft;
skipEmpty = se;
}
}
public static settingsSet fileTypeA = new settingsSet(1, "txt", true);
public static settingsSet fileTypeB = new settingsSet(10, "csv", false);
public static settingsSet fileTypeC = new settingsSet(3, "hex", true);
}
You can even make it written more like your static class:
public static class settingModule {
public struct settingsSet {
public int startingRow;
public string fileType;
public bool skipEmpty;
}
public static readonly settingsSet fileTypeA = new settingsSet {
startingRow = 1,
fileType = "txt",
skipEmpty = true
};
public static readonly settingsSet fileTypeB = new settingsSet {
startingRow = 10,
fileType = "csv",
skipEmpty = false
};
public static readonly settingsSet fileTypeC = new settingsSet {
startingRow = 3,
fileType = "hex",
skipEmpty = true
};
}
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; }
}
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".