Alternative to nesting enums - c#

I'm trying to create several enums as such, that gives the syntax of Dropdown.Category.Subcategory. However, I have been reading that this isn't such a good idea. My choice for this was mostly because I couldn't think of any other way to select different enum values depending on the choice of the category, and then the choice of the subcategory is subject to the selected enum based on the enum values.
Is there a better way to create such functionality? I would prefer to be able to easily identify both the .Category and .Subcategory names, and it would be a bonus if this code was readable.
Just to make it clear, I want to be able to choose the Category, then have an appropriate Subcategory selection.
public class Dropdown
{
public enum Gifts
{
GreetingCards,
VideoGreetings,
UnusualGifts,
ArtsAndCrafts,
HandmadeJewelry,
GiftsforGeeks,
PostcardsFrom,
RecycledCrafts,
Other
}
public enum GraphicsAndDesign
{
CartoonsAndCaricatures,
LogoDesign,
Illustration,
EbookCoversAndPackages,
WebDesignAndUI,
PhotographyAndPhotoshopping,
PresentationDesign,
FlyersAndBrochures,
BusinessCards,
BannersAndHeaders,
Architecture,
LandingPages,
Other
}
}

Create a class that cannot be inherited from externally, give it several inner classes, each extending from it. Then add static read only variables for each of the values that you want to represent:
public class Dropdown
{
private string value;
//prevent external inheritance
private Dropdown(string value)
{
this.value = value;
}
public class Gifts : Dropdown
{
//prevent external inheritance
private Gifts(string value) : base(value) { }
public static readonly Dropdown GreetingCards =
new Gifts("GreetingCards");
public static readonly Dropdown VideoGreetings =
new Gifts("VideoGreetings");
public static readonly Dropdown UnusualGifts =
new Gifts("UnusualGifts");
public static readonly Dropdown ArtsAndCrafts =
new Gifts("ArtsAndCrafts");
}
public class GraphicsAndDesign : Dropdown
{
//prevent external inheritance
private GraphicsAndDesign(string value) : base(value) { }
public static readonly Dropdown CartoonsAndCaricatures =
new GraphicsAndDesign("CartoonsAndCaricatures");
public static readonly Dropdown LogoDesign =
new GraphicsAndDesign("LogoDesign");
public static readonly Dropdown Illustration =
new GraphicsAndDesign("Illustration");
}
public override string ToString()
{
return value;
}
}
In this case every single value is actually an instance of type Dropdown, so you could have, say, a parameter to a method that accepts a Dropdown instance. With enums there is no way to say, "I want to accept any of the enums declared in the Dropdown class."
Here is some example usage:
public static void UseDropdown(Dropdown type)
{
if (type is Dropdown.Gifts)
{
if (type == Dropdown.Gifts.GreetingCards)
{
DoStuff();
}
}
else if (type is Dropdown.GraphicsAndDesign)
{
}
}
You could also have a parameter that accepts an object of type Gifts or GraphicsAndDesign, if you only want a sub-type to be valid in some context.
Sadly, using this solution there's no good way to switch on a dropdown value; you have to just use if/else if chains to check the values.
The use of an instance string value may not be required (see the first revision for a version without it) but it can be very helpful to be able to have a meaningful string value (or other kind of value; you can associate an integer, a byte, or whatever with each enumeration value).
The Equals and GetHashCode implementations should be meaningful if left without being overridden.
You can implement IComparable if the items should be logically ordered somehow, like real enums.

Related

how to handle lots of separate ui commands c#, how to get them, initialize them and execute them

I have fairly complicated ui system that i want to simplify. What I want is to make separate classes with command and fluent builder pattern that which instances I will create at the beginning of the app and then execute when needed, something like this:
public interface IGuiCommand
{
void Execute();
void Execute(Action onComplete = null);
}
public class GuiCommandBuilder : IGuiCommand
{
public void Execute()
{
}
public void Execute(Action onComplete = null)
{
}
}
public class GuiCommandTest : GuiCommandBuilder
{
private int _id;
public GuiCommandTest()
{
}
public GuiCommandTest Setup(int id)
{
_id = id;
return this;
}
}
so I can do this:
// create command
var GuiRoomSelectionCommand = new GuiCommandTest();
// setup and execute (I have to provide additional data in order
// command to work correctly, some commands don't need additional data)
GuiRoomSelectionCommand
.Setup(223)
.Execute(() =>
{
});
The problem I am having is that I want to create all commands
at the start of the app and store them for later easy access, but I can't just
store them as IGuiCommand since I will need a concrete instance in order to setup the
concrete commands? How to do that, or if any other setup is better approach?
I want that others can easily see what commands are there and how to add more of them
if needed or easily modify the existing ones in separate classes so that main controller
doesn't change to much but only specific commands
Define an ICommandParameter interface to indicate the name of the parameter and its current value.
You can create different types of parameters by implementing this interface:
public ICommandParameter
{
string Name {get;set;}
object ObjectValue {get;set;}
}
public IDoubleParameter : ICommandParameter
{
double MinValue {get;set;}
double MaxValue {get;set;}
double Number {get;set;}
int DecimalsCount {get;set;}
}
public IBoolParameter : ICommandParameter
{
bool Checked {get;set;}
}
public IAnimalsParameter : ICommandParameter
{
Animal Animal {get;set;} // Animal is an enum with a list of values
}
Now, your IGuiCommand can have a list of ICommandParameter that you can initialize in the constructor of your command.
In GuiCommandBuilder (or in other class if you consider) you can manage the parameters. You have access to the command parameters and show/update using some Forms.
For example, in your GuiRoomSelectionCommand you can access to the parameters list, with a simgle parameter "Number of rooms" that you can see (you can check/try cast) that implements IDoubleParameter with this values:
Name = "Number of rooms"
ObjectValue = 2
MinValue = 1
MaxValue = 10
Number = 2 // Usually a cast of ObjectValue interface
DecimalsCount = 0
You can create a Form in which you add some controls depending of parameters type. For previous example, you can add a Label for Name and a NumericUpDown for the value (settings decimals, minimum and maximum). You initialize the numeric with the Number property. And when you click Ok in the dialog, you can update de Number property with the numeric value.
For any Enum property, you can add a ComboBox to show/edit it's value. For a bool property, a CheckBox and so on.
You need spend some time to create the controls for any type of property to manage but, at the end, most of properies are of basic types. There aren't lots of controls.

Subclassing dictionary for use in external components

I have a dictionary class which is used to store data, and which tracks hundreds of variables during the course of a session.
I have been tasked with building a common framework, which I can reference this base set of functionality, but allow the calling components to use different key and value within the dictionary.
Currently, this dictionary uses a 4-part tuple as the key, and a 2-part value.
The two components I'm tying into has a different key and value layout.
Component 1 - key is a 5-part tuple, and value is a 3-part.
Component 2 - Key is a 3-part tuple (string, int, string) and the same 2-part value.
This class handles data transfer to other components, so to avoid duplication of effort, want to keep as much of the common functionality in the Common dll, and external components would use the Survey class which the different key/value. Not certain I'm explaining it well enough.
I have included the current code below.
Seems to me, if the main Survey is created with object, object, and subclass the external components with the correct key/value pair.
public sealed class Survey
{
#region Private Objects
private Survey()
{
}
private Dictionary<SurveyKey, SurveyValue> survey = new Dictionary<SurveyKey, SurveyValue>();
private int maxLines = 50000;
private bool AllowLogging { get => (survey.Count > maxLines); }
#endregion
private void WriteData(SurveyKey key, SurveyValue value)
{
if (AllowLogging)
{
if (!survey.ContainsKey(key))
survey.Add(key, value);
else
survey[key] = value;
}
}
}
#region SurveyValue Class
public sealed class SurveyValue
{
public SurveyValue(int? value = null, string detail = null)
{
Detail = detail;
Value = value;
}
// Uses an either/or value; Value or Detail
public string Detail { get; private set; }
public int? Value { get; private set; }
}
#endregion
#region SurveyKey Class
public sealed class SurveyKey : Tuple<string, string, string, string>
{
public SurveyKey(string Signal, string SignalType, string Name, string OverallType) : base(Signal, SignalType, Name, OverallType) { }
public string Signal { get => Item1; }
public string SignalType { get => Item2; }
public string Name { get => Item3; }
public string OverallType { get => Item4; }
}
Make your common class generic of type K,V and use the where keyword to restrict the dictionary K and V to KBaseClass and VBaseClass. Component1 can expose KBaseClass and VBaseClass derived types and inherit from common or reuse common.
Turns out, I was over thinking this problem. All I need to do is create my base dictionary as Survey, and use this in my external components.
For some reason I was thinkning I needed to create an interface to allow the plugging in of the base dictionary.
private Dictionary<TKey, TValue> survey = new Dictionary<TKey, TValue>();

How do you make an 'enum' that has data tied to it?

I have a Vote class and one of the properties it can have is a vote type. Such as unanimous, a 3/4 vote, a simply majority, etc. Each type needs to have a string associated with it which will describe the vote type (like "A simply majority requires 51% to pass" etc.). I need to pass these vote types/description in with my view model to my view and then I can make my drop down list with it.
Then, when the form that creates the vote is submitted I just need to bind the vote type (without description) to the Vote model (which is part of the view model).
I've only been using C# for a short time and I don't quite understand how the enums work in it. Perhaps enum is not the way to go about this.
public class VoteViewModel
{
public VoteViewModel()
{
Vote = new Vote();
}
public Vote Vote { get; set; }
public int EligibleVoters { get; set; }
}
And this is where I'll be putting the drop down.
<section class="vote-type">
<select name="">
<option value="">Select Vote Type</option>
</select>
<section class="vote-type-info">
<p class="vote-rules">To pass this vote, at least 51% of Eligible Voters must vote to approve it.</p>
</section>
</section>
Please notice I'm only showing for strings for it could be any type. In each case I mention how to extend it for more values if possible.
Using the enum as a key
You can use your enum type as a key for a dictionary (you want to be unique, so make it static and readonly in some helper class):
private static readonly Dictionary<MyEnum, string> _dict =
{
//Using dictionary initialization
{MyEnum.MyValue, "The text for MyValue"},
{MyEnum.MyOtherValue, "Some other text"},
{MyEnum.YetAnotherValue, "Something else"}
}
public static readonly Dictionary<MyEnum, string> Dict
{
get
{
return _dict;
}
}
And access the associated value:
string text = Dict[MyEnum.MyValue];
Or with:
string text;
if (Dict.TryGetValue(MyEnum.MyValue, out text))
{
//It has the value
}
else
{
//It doesn't have the value
}
This way you can access a string that is associated with the enum value. Then you can expose your Dictionary so that you can read the corresponding values.
You will need a complex type for storing more than one value. Just use your custom type isntead of string. Or if available you can use Tuples.
Accesing the Dictionary may mean an extra annoyance and hopefully it will not mean a threading problem too.
Enum.GetName
You can use Enum.GetName to read the name of the values of your enum:
string text = Enum.GetName(MyEnum.MyValue);
//text will have the text "MyValue"
//or
var some = MyEnum.MyValue;
string text = Enum.GetName(some);
Note: ToString() should work too.
Sadly, this will not work for something else than the string.
Also it has the drawback that you cannot put any text there (it has to be a valid identifier).
Custom Attributes
You will have to declare an attribute type:
[AttributeUsage(AttributeTargets.Field)]
public class EnumValueAttribute : System.Attribute
{
public readonly string _value;
public string Value
{
get
{
return _value;
}
}
public EnumValueAttribute(string value) // value is a positional parameter
{
//beware: value can be null...
// ...but we don't want to throw exceptions here
_value = value;
}
}
Now you apply the attribute to your enum:
public enum MyEnum
{
[EnumValue("The text for MyValue")]
MyValue = 1,
[EnumValue("Some other text")]
MyOtherValue = 2,
[EnumValue("Something else")]
YetAnotherValue = 3
}
Lastly you will need to read the attribute back:
public static string GetValue(MyEnum enumValue)
{
FieldInfo fieldInfo = typeof(MyEnum).GetField(enumValue.ToString());
if (!ReferenceEquals(fieldInfo, null))
{
object[] attributes = fieldInfo.GetCustomAttributes(typeof(EnumValueAttribute), true);
if (!ReferenceEquals(attributes, null) && attributes.Length > 0)
{
return ((EnumValueAttribute)attributes[0]).Value;
}
}
//Not valid value or it didn't have the attribute
return null;
}
Now you can call it:
string text1 = GetValue(MyEnum.MyValue);
//text1 will have the text "MyValue"
//or
var some = MyEnum.MyValue;
string text2 = GetValue(some);
You can add more fields to your attribute class and use them to pass any other value you may need.
But this requires reflexion, and it may not be available if you are running in a sandbox. Also it will retrieve the attributes each time, creating some short lived objects in the proccess.
Emulate Enum
You can emulate an enum with a sealed class that has no public constructor and exposes static readonly instances of itself:
public sealed class MyEnumEmu
{
private static readonly string myValue = new MyEnumEmu("The text for MyValue");
private static readonly string myOtherValue = new MyEnumEmu("Some other text");
private static readonly string yetAnotherValue = new MyEnumEmu("Something else");
public static MyEnumEmu MyValue
{
get
{
return myValue;
}
}
public static MyEnumEmu MyOtherValue
{
get
{
return myOtherValue;
}
}
public static MyEnumEmu YetAnotherValue
{
get
{
return yetAnotherValue;
}
}
private string _value;
private MyEnumEmu(string value)
{
//Really, we are in control of the callers of this constructor...
//... but, just for good measure:
if (value == null)
{
throw new ArgumentNullException("value");
}
else
{
_value = value;
}
}
public string Value
{
get
{
return _value;
}
}
}
Use it as always:
var some = MyEnumEmu.MyValue;
And access the associated value:
string text = MyEnumEmu.MyValue.Value;
//text will have the text "MyValue"
//or
string text = some.Value;
This is the more flexible of all, you can either use a complex type instead of string or add extra fields for passing more than a single value.
But... it is not really an enum.
You could create a "constant" dictionary (or rather readonly static, since you can't create a constant dictionary) around your Enum.
public enum VoteType { Unanimous = 1, SimpleMajority = 2, ... }
public static readonly Dictionary<VoteType, string> VoteDescriptions = new Dictionary<VoteType, string>
{
{ VoteType.Unanimous, "Unanimous description" },
{ VoteType.SimpleMajority, "Simple majority" },
...
};
public class Vote()
{
public VoteType VoteSelectType { get; set; }
}
public enum VoteType
{
[Display(Name = "Enter Text Here")]
unanimous = 1,
[Display(Name = "Enter Text Here")]
threequatervote = 2,
[Display(Name = "Enter Text Here")]
simplymajority = 3
}
Goto here this is pretty much your solution
How do I populate a dropdownlist with enum values?
You can use enums if you want but you need to decide how to make the link between the enum value and what you want to display. For example, an enum value of SimpleMajority you would want displayed as "Simple Majority". One way to do this is using the Description attribute and a helper class as described here.
However, you might find it easier to set up a lightweight collection class to store vote type values and their description. This could be as simple as a Dictionary<int, string> You will probably find this a more straightforward approach.
Since you have the type and description I'll better suggest you to create a class that wraps up both instead of enum. The advantage is you can reduce more work and it's very flexible.
public class VoteType
{
public string Name{ get; set; }
public string Description{ get; set; }
}
Now your Vote class will have reference to this VoteType.
public class Vote
{
...
public VoteType Type{ get; set; }
}
In your VoteViewModel you better have a class that contains all the VoteTypes.
public class VoteViewModel
{
...
public IEnumerable<SelectListItem> VoteTypes{ get; set; }
}
Now you can easily bind the VoteTypes in a dropdownlist.
#model VoteViewModel
#Html.DropDiwnListFor(m => m.VoteTypes,...)
I have used this before, it is really handy.
http://www.codeproject.com/Articles/13821/Adding-Descriptions-to-your-Enumerations
In short what it lets you do is:
public enum MyColors{
[Description("The Color of my skin")]
White,
[Description("Bulls like this color")]
Red,
[Description("The color of slime")]
Green
}
and then get the description back by simply calling:
String desc = GetDescription(MyColor.Green);
It does use reflection though, so there is a tradeoff between simplicity and a slight performance hit. Most of the time I'd take the performance hit...

Checking if any property has been assigned a value

I have a type SearchBag that holds a bunch of strings and nullable integers to use for passing on search values. I need a way to check if the search bag contains any values.
I'm currently trying to do it like this:
public bool HasValues()
{
return GetType().GetProperties().Any(p => p.GetValue(this, null) != null);
}
But was wondering if there's a better way.
Without modifying the SearchBag type, there isn't a better way.
EDIT: You could change the type to set a boolean flag in every property setter, then check the flag instead of using Reflection.
You could use Post Sharp to intercept the request to change a property value. You could have all search classes inherit from a common class with a List<string>. Then create an aspect attribute to add a property name to that dictionary whenever the value changes. The following is just a sample, and has bugs:
[Serializable]
public class PropertyChangeAwareAttribute : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionEventArgs eventArgs)
{
if (eventArgs.Method.Name.StartsWith("set_"))
((SearchBagBase)eventArgs.Instance).PropertiesChanged.Add(eventArgs.Method.Name);
base.OnEntry(eventArgs);
}
}
abstract class SearchBagBase
{
public List<string> PropertiesChanged = new List<String>();
}
[PropertyChangeAware]
class RegularSearch : SearchBagBase
{
public String Key { get; set; }
}
with usage:
RegularSearch regularSearch = new RegularSearch();
regularSearch.Key = "toys";
regularSearch.PropertiesChanged.ForEach(Console.WriteLine);

Dot notation for hierarchical related string values

I have a lot of constant string values in my application which I want to have as strongly typed objects in C# for code reuse and readability. I would like to be able to reference the string value like so:
Category.MyCategory //returns a string value ie “My Category”
Category.MyCategory.Type.Private //returns a string value ie “private”
Category.MyCategory.Type.Shared //returns a string value ie “shared”
I have started by implementing the following classes each containing a list of public string valued fields with a public property which exposes the child.
Category, MyCategory, Type
However I already know this is not the way to go so could do with a bit of advice on this one.
An example of this is where I am using the Syndication classes to add a category to an atom feed. I am creating the items in this feed dynamically so need to use the notation as shown.
item.Categories.Add( new SyndicationCategory
{
Scheme = Category.PersonType,
Label="My Category",
Name=Category.MyCategory.Type.Private
});
Keep your string constants close to where you need them, IMO having a class that just declares constants is an OO antipattern
Why not simply implement them as classes with overridden ToString implementations?
public class MyCategory
{
private readonly MyType type;
public MyCategory()
{
this.type = new MyType();
}
public MyType Type
{
get { return this.type; }
}
// etc.
public override string ToString()
{
return "My Category";
}
}
public class MyType
{
public override string ToString()
{
return "My Type";
}
// more properties here...
}
However, for general purposes, consider whether the strings in themselves don't represent concepts that are better modeled as full-blown objects.
I completely agree with Rob. If you still want to have a "bag of strings", you could try using nested classes, something like below. I don't really like it, but it works.
public class Category
{
public class MyCategory
{
public const string Name = "My Category";
public class Type
{
public const string Private = "private";
public const string Shared = "shared";
}
}
}

Categories

Resources