|
Can we assume that this class will allow us to add another value to the enum class without breaking the original enum values?
I have an API that I cannot change that exposes an enum with a set of values, for example...
public enum API
{
ValueOne,
ValueTwo
}
Can I use this class to extend API to...
public class MyAPI : EnumBaseType<api>
{
public static readonly Rating ValueOne = new Rating(1, "ValueOne", false);
public static readonly Rating ValueTwo = new Rating(2, "ValueTwo", false);
public static readonly Rating ValueThree = new Rating(3, "ValueThree", false);
...
Efective;y chnaging the base value and adding additional value, also can I compare my extended enum with the original enum...
API api = API.ValueOne;
MyAPI myAPI = MyAPI.ValueThree;
if (API == MyAPI)
{
// do something
}
Feed The Wee Turtle(s)...
|
|
|
|
|
Hello
First thank you for this, thanks for sharing it.
I am using it, it works perfectly but when I want to use my application under Windows Server 2008, sometime it works sometime not. Under Windows 7 or Windows 8 it works.
I have been comparing the code etc... I cannot understand what is wrong, any idea?
This one will not work, it returns no values
<pre lang="c#"> [Serializable()]
public class RCU_Types : EnumBaseType<RCU_Types>
{
[NonSerialized()] public static readonly RCU_Types Relay = new RCU_Types(0X01, "Relay", RCU_Output_Modes.Interrupter, RCU_Output_Modes.PulseInterrupter);
[NonSerialized()] public static readonly RCU_Types Triac = new RCU_Types(0X05, "Triac", RCU_Output_Modes.Interrupter, RCU_Output_Modes.PulseInterrupter, RCU_Output_Modes.LightDimmer);
public readonly Hashtable Supported_Output_Modes;
public RCU_Types(int key, string value, params RCU_Output_Modes[] supported_output_modes) : base(key, value)
{
this.Supported_Output_Modes = new Hashtable();
foreach(RCU_Output_Modes output_mode in supported_output_modes)
{
this.Supported_Output_Modes.Add(output_mode.Key, output_mode);
}
}
public static ReadOnlyCollection<RCU_Types> GetValues()
{
return GetBaseValues();
}
public static RCU_Types GetByKey(int key)
{
return GetBaseByKey(key);
}
public static int Number_Of_RCU_Types()
{
return enumValues.Count;
}
}
This one will work:
[Serializable()]
public class RCU_Commands : EnumBaseType<RCU_Commands>
{
[NonSerialized()] public const Byte MIN_LUMINOSITY = 0;
[NonSerialized()] public const Byte MAX_LUMINOSITY = 100;
[NonSerialized()] public static readonly RCU_Commands TurnOn = new RCU_Commands(0X10, "TurnOn", false);
[NonSerialized()] public static readonly RCU_Commands TurnOff = new RCU_Commands(0X11, "TurnOff", false);
[NonSerialized()] public static readonly RCU_Commands SmoothTurnOn = new RCU_Commands(0X12, "SmoothTurnOn", false);
[NonSerialized()] public static readonly RCU_Commands SmoothTurnOff = new RCU_Commands(0X13, "SmoothTurnOff", false);
[NonSerialized()] public static readonly RCU_Commands Dim = new RCU_Commands(0X14, "Dim", false);
[NonSerialized()] public static readonly RCU_Commands Bright = new RCU_Commands(0X15, "Bright", false);
[NonSerialized()] public static readonly RCU_Commands SimulationTurnOn = new RCU_Commands(0X70, "SimulationTurnOn", false);
[NonSerialized()] public static readonly RCU_Commands SimulationTurnOff = new RCU_Commands(0X71, "SimulationTurnOff", false);
[NonSerialized()] public static readonly RCU_Commands EnableReArmer = new RCU_Commands(0X72, "EnableReArmer", false);
[NonSerialized()] public static readonly RCU_Commands DisableReArmer = new RCU_Commands(0X73, "DisableReArmer", false);
[NonSerialized()] public static readonly RCU_Commands ResetReArmer = new RCU_Commands(0X74, "ResetReArmer", false);
[NonSerialized()] public static readonly RCU_Commands InitiateTransfer = new RCU_Commands(0X7F, "InitiateTransfer", false);
[NonSerialized()] public static readonly RCU_Commands SetTime = new RCU_Commands(0X8301, "SetTime", true);
[NonSerialized()] public static readonly RCU_Commands SetDayOfWeek = new RCU_Commands(0X8104, "SetDayOfWeek", true);
[NonSerialized()] public static readonly RCU_Commands SetTemperature = new RCU_Commands(0X8205, "SetTemperature", true);
[NonSerialized()] public static readonly RCU_Commands SetLight = new RCU_Commands(0X8108, "SetLight", true);
[NonSerialized()] public static readonly RCU_Commands DisplayPredefinedMessage = new RCU_Commands(0X810A, "DisplayPredefinedMessage", true);
[NonSerialized()] public static readonly RCU_Commands DisplayMessage = new RCU_Commands(0XFF0B, "DisplayMessage", true);
[NonSerialized()] public static readonly RCU_Commands ChangeLuminosity = new RCU_Commands(0X8120, "ChangeLuminosity", true);
[NonSerialized()] public static readonly RCU_Commands ChangeLuminosityGradually = new RCU_Commands(0X8121, "ChangeLuminosityGradually", true);
[NonSerialized()] public static readonly RCU_Commands SetID = new RCU_Commands(0X8170, "SetID", true);
[NonSerialized()] public static readonly RCU_Commands SetOutputMode = new RCU_Commands(0X8171, "SetOutputMode", true);
[NonSerialized()] public static readonly RCU_Commands SetSimulationSupport = new RCU_Commands(0X8172, "SetSimulationSupport", true);
public readonly bool Value_Valid;
public RCU_Commands(int key, string value, bool value_valid) : base(key, value)
{
this.Value_Valid = value_valid;
}
public static ReadOnlyCollection<RCU_Commands> GetValues()
{
return GetBaseValues();
}
public static RCU_Commands GetByKey(int key)
{
return GetBaseByKey(key);
}
public static int Number_Of_Commands()
{
return enumValues.Count;
}
}
|
|
|
|
|
I am actually glad we are not the only ones that experience rare instability with this implementation. We have a shared DLL that uses EnumBaseType<t> on both client and IIS WCF hosted service. Here is what happens for some users on some machines. I am really wondering if this could be specific to Windows Server 2008, since you mentioned it and we are also using it in production.
So, here is the flow
1. client application makes a call to the service
2. the first access to the method GetByKey(string key) that calls the base GetBaseByKey(string key) will return enumValues having 0 items (no null) and GetByKey will fail, since there no items
3. the error is returned to the client, saying it could not find the value in the enumeration (I used try catch inside GetBaseByKey to return more descriptive error saying that the Key was not found in the list of elements and then I list the elements that are currently in the enumValues.
So, the error says something like this "Could nof find key "MyKey" in the collection of keys "".)
4. the second call will have no error and will return the value properly.
5. I tried to reproduce the problem in various unit tests, attacking it by creating multiple instance, using reflection, starting parallel threads. There was no way I could not reproduce the problem.
So, we had to fix the problem without being able to reproduce it and it seems like the solution we found it has been workng ok for a couple of months now.
For every enumeration we have, we have implemented a verification that when GetByKey is called, it will check for the enumValues count and if it's zero, it will assign each of the enum elements to a temp variable. This way it will trigger/initiates the collection if it's missing.
Assuming we have a enum MyTestEnum with 4 EnumValues:
public static MyTestEnum GetByKey(string key)
{
if(enumValues.Count < 1)
{
var tmp = MyTestEnum.EnumValue1;
tmp = MyTestEnum.EnumValue2;
tmp = MyTestEnum.EnumValue3;
tmp = MyTestEnum.EnumValue4;
}
return GetBaseByKey(key);
}
I am not so happy about this solution and I am thinking about getting away from static and using a singleton. It will add additional dotInstance to the usage but I think it would be worth eliminating the static data from it. So it would look something like this:
var a = MyTestEnum.Instance.EnumValue1;
and
var enumElement = MyTestEnum.Instance.GetByKey("somekey");
it's not as neat as it was before, but that's the sacrifice is worh making, I believe.
Any thought on that?
[tridy]
Stockholm, Sweden
|
|
|
|
|
I've tried a similar solution and was eventual stymied by two rules and one fact ...
Fact 1) Some people like to use switch statements on their enums.
Rule 1) Case statements have to use const values.
Rule 2) Only built in value types can be constant.
So this will not work:
Rating myRating = Rating.Awesome;
switch (myRating)
{
case Rating.Awesome:
break;
case Rating.Awful:
break;
case Rating.Bad:
break;
default:
break;
}
I think that this means that using your or any other class replacement for enums will therefor always fall a little short.
|
|
|
|
|
Very true but you could override the GetHashCode() to return the Key value and do a switch statement on the Key.
|
|
|
|
|
Hi,
I have used this same technique because I really liked the way Java deals with enums. I did have a couple comments though. First, your base class forces an integer key type. It would be better if this could be any type. Second, you are not doing anything to enforce uniqueness on the key or value.
I have implemented the same sort of thing using a couple levels.
First, I have an EnumType<T> class that does what you have above but does not require a key. The the class is a reference type, the objects reference essencially becomes its key. The only thing the constuctor takes is a string called name (same as your value). The names are required to be unique. This class does not implement GetBaseByKey since there is no key.
Second, I have a ComparableEnumType<T, ValueType> : where ValueType : IComparable<ValueType>. The provides a key / value pair and the ability to get the enum instance from the ValueType key.
I also have a skeleton for a EquatableEnumType<T, ValueType> which is not yet fully implemented but would extend things further.
The code below has some of the problems that you address in your article, which I am going to be incorperating. So thanks.
<code>
/// <summary>
/// Base class for implementing enums as objects.
/// </summary>
/// <typeparam name="T">The type of the enum.
/// Must extend EnumType(<i>T</i></typeparam>
public abstract class EnumType<T> where T : EnumType<T>
{
#region Static Fields
/////////////////////////////////////////////////////////////////
// ------------------------ Static Fields ---------------------
/////////////////////////////////////////////////////////////////
/// <summary>Maintains the list of enum objects.</summary>
private static List<T> values = new List<T>();
/// <summary>Maintains the map of enum objects refereneced by their names.</summary>
private static Dictionary<String, T> nameList = new Dictionary<string, T>();
#endregion
#region Static Methods
/////////////////////////////////////////////////////////////////
// ------------------------ Static Methods --------------------
/////////////////////////////////////////////////////////////////
/// <summary>
/// Gets a read-only collection of the enum objects in the type.
/// </summary>
/// <returns>a read-only collection of the enum objects in the type</returns>
public static ReadOnlyCollection<T> Values {
get {
return values.AsReadOnly();
}
}
/// <summary>
/// Gets the enum object associated with the name passed.
/// </summary>
/// <param name="name">the name of the object to return</param>
/// <returns>the object referenced by the name passed,
/// null if no such object exists.</returns>
public static T ValueOf(String name) {
try {
return nameList[name];
} catch (KeyNotFoundException) {
return null;
}
}
#endregion
#region Non-Public Fields
/////////////////////////////////////////////////////////////////
// ------------------------ Non-Public Fields -----------------
/////////////////////////////////////////////////////////////////
private String name;
#endregion
#region Properties
/////////////////////////////////////////////////////////////////
// ------------------------ Properties ------------------------
/////////////////////////////////////////////////////////////////
/// <summary>
/// The String representation of the enum value.
/// </summary>
public String Name {
get {
return name;
}
}
#endregion
#region Constructors
/////////////////////////////////////////////////////////////////
// ------------------------ Constructors ----------------------
/////////////////////////////////////////////////////////////////
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="name">the name of the enum object</param>
protected EnumType(String name) {
if (nameList.ContainsKey(name)) {
throw new ArgumentException (
String.Format("Class {0} already contains an element with name \"{1}\".",
typeof(T).Name, name));
}
this.name = name;
values.Add((T)this);
nameList.Add(name, (T)this);
}
#endregion
#region Class Base Overrides
/////////////////////////////////////////////////////////////////
// ------------------------ Class Overrides -------------------
/////////////////////////////////////////////////////////////////
/// <summary>
/// Gets the string representation of the enum object. This is
/// the same as the object's name.
/// </summary>
/// <returns>the string representation of the enum object</returns>
public override string ToString() {
return name;
}
#endregion
#region Public Methods
/////////////////////////////////////////////////////////////////
// ------------------------ Public Methods --------------------
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/// <summary>
/// Executes a switch statement.
/// </summary>
/// <param name="cases">the cases to check</param>
/////////////////////////////////////////////////////////////////
public void Switch(EnumSwitchCase[] cases) {
foreach (EnumSwitchCase currentCase in cases) {
if (currentCase.caseValue == null ||
currentCase.caseValue.Equals(this)) {
currentCase.executable();
break;
}
}
}
#endregion
}
/// <summary>
/// Implements a base class for enum objects where each object is
/// associated with a comparable value. This allows for the conversion
/// from an instance of a value to the appropriate enum object.
/// </summary>
/// <typeparam name="T">The type of the enum.
/// Must extend ComparableEnumType(<i>T</i>, <i>ValueType</i>)</typeparam>
/// <typeparam name="ValueType">The type of the comparable objects
/// that are associated with each enum object.
/// Must implement IComparable(<i>ValueType</i>)</typeparam>
public abstract class ComparableEnumType<T, ValueType> : EnumType<T>, IComparable<T>
where T : ComparableEnumType<T, ValueType>
where ValueType : IComparable<ValueType>
{
#region Inner Classes / Types
/////////////////////////////////////////////////////////////////
// ------------------------ Inner Classes ---------------------
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/// <summary>
/// A method that may be called to test to test if two values
/// are equal.
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns>if the values passed are equal.</returns>
/////////////////////////////////////////////////////////////////
public delegate bool EqualsTesterDelegate(ValueType a, ValueType b);
#endregion
#region Static Fields
/////////////////////////////////////////////////////////////////
// ------------------------ Static Fields ---------------------
/////////////////////////////////////////////////////////////////
/// <summary>
/// Method that is called to compare values. If null, the standard
/// comparable method will be called.
/// </summary>
protected static EqualsTesterDelegate AreEqual = null;
/// <summary>
/// the enumeration with the highest comparable value.
/// </summary>
private static T max = null;
/// <summary>
/// Gets the enumeration with the highest comparable value.
/// </summary>
public static T Max {
get { return max; }
}
/// <summary>
/// the enumeration with the lowest comparable value.
/// </summary>
private static T min = null;
/// <summary>
/// Gets the enumeration with the lowest comparable value.
/// </summary>
public static T Min {
get { return min; }
}
#endregion
#region Static Methods
/////////////////////////////////////////////////////////////////
// ------------------------ Static Methods --------------------
/////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
/// <summary>
/// Gets the instance associated with the <i>value</i> passed. If
/// an instance is not found to match the value passed, an attempt
/// to get the default value for the type is made. If a default
/// instance is found, that is returned, otherwise null is returned.
/// </summary>
/// <param name="value">the value to be assessed</param>
/// <returns>the instance associated with the <i>value</i> passed,
/// null or default instance if none found.</returns>
/////////////////////////////////////////////////////////////////
public static T Convert(ValueType value) {
foreach (T result in Values) {
if (AreEqual != null) {
if (AreEqual(value, result.Value)) {
return result;
}
}
else if (result.Value != null &&
result.Value.CompareTo(value) == 0) {
return result;
}
}
return GetDefault();
}
/////////////////////////////////////////////////////////////////
/// <summary>
/// Gets the instance associated with the <i>value</i> passed. If
/// an instance is not found to match the value passed, <i>defaultVal</i>
/// is returned.
/// </summary>
/// <param name="value">the value to be assessed</param>
/// <param name="defaultVal">the instance to return if there is
/// no match made</param>
/// <returns>the instance associated with the <i>value</i> passed,
/// <i>defaultVal</i> if none found.</returns>
/////////////////////////////////////////////////////////////////
public static T Convert(ValueType value, T defaultVal) {
foreach (T result in Values) {
if (AreEqual(value, result.Value)) {
return result;
}
}
return defaultVal;
}
/////////////////////////////////////////////////////////////////
/// <summary>
/// Gets the default value of the derived class if defined.
/// Otherwise returns null.
/// </summary>
/// <returns>the default value of the derived class if defined,
/// Otherwise returns null</returns>
/// <remarks>For a derived type to have a default value that will
/// be returned by this method, the type must implement a static
/// methods named <b>"GetDefault"</b> which takes no arguments
/// and returns the default member of the derived type.</remarks>
/////////////////////////////////////////////////////////////////
public static T GetDefault() {
// Get type of derived class
Type t = typeof(T);
// get all public static methods
MethodInfo[] methods = t.GetMethods(BindingFlags.Static | BindingFlags.Public);
try {
// See if GetDefaultValue exists
foreach (MethodInfo method in methods) {
if (method.Name.Equals("GetDefault")) {
// get the default value
return (T)method.Invoke(null, null);
}
}
}
catch (Exception) {
}
// No GetDefault method found, returning null
return null;
}
#endregion
#region Public Fields
/////////////////////////////////////////////////////////////////
// ------------------------ Public Fields ---------------------
/////////////////////////////////////////////////////////////////
/// <summary>
/// The value associated with this object.
/// </summary>
public readonly ValueType Value;
#endregion
#region Constructors
/////////////////////////////////////////////////////////////////
// ------------------------ Constructors ----------------------
/////////////////////////////////////////////////////////////////
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="name">the name of the enum object</param>
/// <param name="value">the value associated with this object,
/// may be null</param>
protected ComparableEnumType(String name, ValueType value) : base (name){
Value = value;
if (max == null || max.Value == null || max.Value.CompareTo(value) < 0) {
max = (T)this;
}
if (min == null || min.Value == null || min.Value.CompareTo(value) > 0) {
min = (T)this;
}
}
#endregion
#region IComparable<T> Members
/////////////////////////////////////////////////////////////////
/// <summary>
/// Compares the current object with another object of the same type.
/// </summary>
/// <param name="other">An object to compare with this object</param>
/// <returns>A 32-bit signed integer that indicates the relative
/// order of the objects being compared</returns>
/////////////////////////////////////////////////////////////////
public int CompareTo(T other) {
return this.Value.CompareTo(other.Value);
}
#endregion
}
</code>
John Butler
|
|
|
|
|
Hi John,
Your method looks interesting.
It would be nice if you wrote an article or a blog post about it.
Regards,
Cassio Alves
|
|
|
|
|
The static methods on System.Enum works fine for me.
|
|
|
|
|
Hi,
I guess you missed the point.
Iterating through the values of an enum is just one example of what we could do if enums were extensible as regular classes, just like it is in Java.
Regards,
Cassio Alves
|
|
|
|
|
I have very little knowledge of Java (although I've read several books about refactoring and design patterns based on the Java language and I know that most of our Nxxxx goodies are borrowed from the Java domain).
I have missed enum extensibility a few times myself, but I have sort of accepted that enums are not extensible, and I have always been able to find a workaround in those rare cases. Of course what you do is ok to do if it's appropriate. I understand enums as simple value types and as such they don't really need any kind of extension.
You can iterate through System.Enum.GetNames(typeof(MyEnum)) or System.Enum.GetValues(typeof(MyEnum)) so I think that your example is an example where you should stick to just using enums and keep the benefit of communicating to other developers that they are dealing with simple value types.
I personally try to stick to the Design guidelines for developing class libraries[^] as much as possible. Of course it's a matter of taste whether to use those conventions or not, but it works fine for me. According to those guidelines your suggestion is an example of incorrect design (Enumeration Design[^]).
I would like to know what other extensibility you miss in the case of enumerations.
A completely other thing is that you are dealing with a kind of "borderline case" where you have to decide whether the matter you're expressing as a type could be regarded as either an enum or a class. The term "borderline case" is my own and covers decisions of whether to express a given matter as enum, struct or class, decisions about whether an attribute should be a method or a property and whether some functionality should be a method or a class (like Kent Beck's method object and the GoF command pattern ).
I find those borderline cases interesting (from a semiotically point of view), and I guess that you are confronted with more of those cases since you use both Java and C#.
Maybe you can use the new VS2008 extension method-functionality to achieve what you want by making a few extensions on System.Enum (works for dotnet 2.0 too as long as you use VS2008 as IDE). That could mean that you would be able to achieve the behavior you want and keep using the enum type as well
-- modified at 1:50 Monday 15th October, 2007
|
|
|
|
|
Hi,
Yes, extension method is something that would come in handy here and I hadn’t thought about that when writing this article.
In no way I am saying we shouldn’t use enums anymore. But, as you mentioned, there are a few times when we miss enum extensibility and we have to come up with workarounds. Well, this is a workaround for the lack of enums extensibility.
As I said in the article, it is not a perfect solution because of the lack of support in the language itself.
Let me bring up again the Java DaysOfWeek example:
enum DaysOfWeek{
SUNDAY,
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY;
public boolean isWeekEnd(){
return (this == SUNDAY || this == SATURDAY);
}
}
I don’t know about you, but I’d rather have the type that defines the days of the week telling me if it’s a weekend or not, than having a helper class just to do that. For me that would be an anti-pattern.
While I may be breaking a c# design guideline, I am not breaking some more basic concepts like The Open Closed Principle or the Principle of Single Responsibility.
But there are many ways to achieve the same result and this is just another one that I think not many people are aware of. So I thought it'd be worth sharing.
Regards,
Cassio Alves
|
|
|
|
|
I am glad you did choose to share it, and those guidelines are only a set of guidelines between many sets of principles, not the law.
I am interested in understanding those cases where stuff can be expressed in different ways, and the title of your article caught my eye.
Beleive me, if I thought your article was nonsense I wouldn't have bothered to make any comment.
Keep sharing
Regards,
Anders Haahr
|
|
|
|
|
It is possible to do a lot with enums by using the static methods on the Enum class, i.e. iterate them ...
Regards,
Thomas Lykke Petersen (MCP)
|
|
|
|
|
Hi,
this is good idea ( not new at all ), but the implementation is not so good
1) It should be struct, not class
2) The static list "enumValues" is not required.
3) The static values can be modified with reflection ( potentialy dangerous ).
This implementation is IMHO cleaner:<code>
/// <summary>
/// Common interface for strongly-typed enums.
/// </summary>
public interface IStronglyTypedEnum {
string GetName();
object GetValue();
}
/// <summary>
/// Generic interface for strongly-typed enums.
/// </summary>
/// <typeparam name="T">Type of value of enum item.</typeparam>
public interface IStronglyTypedEnum<T> : IStronglyTypedEnum {
new T GetValue();
}
/// <summary>
/// Enumeration of commonly used colors.
/// </summary>
public struct Colors : IStronglyTypedEnum<System.Drawing.Color> {
public static Colors Background {
get { return new Colors( "Background", System.Drawing.Color.White ); }
}
public static Colors Text {
get { return new Colors( "Text", System.Drawing.Color.Black ); }
}
public static Colors SelectedText {
get { return new Colors( "SelectedText", System.Drawing.Color.Blue ); }
}
private string name;
public string GetName() { return this.name; }
private System.Drawing.Color value;
public System.Drawing.Color GetValue() { return this.value; }
object IStronglyTypedEnum.GetValue() { return this.value; }
public Colors( string name, System.Drawing.Color value ) {
this.name = name;
this.value = value;
}
/// <summary>
/// Return collection with enum's items.
/// </summary>
public static ICollection<Colors> GetEnumItems() {
Colors[] items = new Colors[]{
Background, Text, SelectedText
};
return items;
}
}</code>
Developer must write more code, but it can be solved with "CustomTool" for VS. Then you can write enum's in XML file for example, and then write code will be much slimer.
---------------------------------
/* Geniality is in simplicity. */
|
|
|
|
|
Very interesting way to extend enums.
|
|
|
|
|
Thank you.
Regards,
Cassio Alves
|
|
|
|
|
public abstract class EnumBaseType<T> where T : EnumBaseType<T><br />
{<br />
public static ReadOnlyCollection<T> GetBaseValues() { ... }<br />
public static T GetBaseByKey(int key) { ... }<br />
}<br />
public class Rating : EnumBaseType<Rating><br />
{<br />
public static ReadOnlyCollection<Rating> GetValues(){ ... }<br />
public static Rating GetByKey(int key){ ... }<br />
<br />
}<br />
And Rating.GetByKey(int) will work fine.
|
|
|
|
|
Unfortunately it can't be done.
Since those are static methods, when you call them they bypass the derived class instantiation.
If you call the GetBaseByKey or GetBaseValues before calling any of the derived class methods or properties, the collection will be empty. That's why you need to implement them on the derived class.
If you use this code things will work fine.
Rating r = Rating.Good;
MessageBox.Show(Rating.GetBaseByKey(3));
But if you remove the first line and call the GetKey method, you will get nothing, because the list was not filled yet.
MessageBox.Show(Rating.GetBaseByKey(3));
Regards,
Cassio Alves
|
|
|
|
|
Yes, you are right!
but if you force Rating initialization in base methods...
<br />
public abstract class EnumBaseType<T> where T : EnumBaseType<T>, new()<br />
{<br />
private static bool s_initialized;<br />
private static void InitializeT()
{<br />
if(!s_initialized)<br />
{<br />
s_initialized = true;<br />
new T();<br />
}<br />
}<br />
public EnumBaseType(int key, string value)<br />
{<br />
Key = key;<br />
Value = value;<br />
enumValues.Add((T)this);<br />
}<br />
protected EnumBaseType()<br />
{<br />
Key = -1;<br />
Value = value;<br />
}<br />
public static ReadOnlyCollection<T> GetBaseValues()<br />
{<br />
InitializeT();<br />
return enumValues.AsReadOnly();<br />
}<br />
It seems too complex
|
|
|
|
|
Yeah, that's nice!
The only problem is that now we have to implement a parameterless constructor on each class.
But I guess it is better than implementing the static methods to work just as a bridge to the base class.
Regards,
Cassio Alves
|
|
|
|
|
I disagree. I think having a public constructor in the enum type allows for instances to be created at run-time other than the instances that make up the enumeration. It also requires a parameterless constructor in the base class that uses an arbitrary key and null value.
I think this is a case where it is better to be same and have to paste a couple methods in the derived classes rather.
John Butler
|
|
|
|
|
yep, this all sounds familier when I wrote
http://www.codeproject.com/useritems/EnumGeneritized.asp
|
|
|
|
|
How about a Dictionary rather than a List?
I suppose with a small enough number of values the performance will be about the same, but using a Dictionary eliminates the loop used to find a value.
|
|
|
|
|
Hi,
A dictionary would probably be a bit faster, but then you would be stuck with key and value only. The point of this article is to show how we can extend the enum functionality, by adding methods and/or properties.
Regards,
Cassio Alves
|
|
|
|
|
I'm not sure you understand, I meant use a Dictionary in your class. Rather than
protected static List<T> enumValues = new List<T>();
use
protected static Dictionary<T,object> enumValues = new Dictionary<T,object>();
You can simply put null in for the value and only use the key . And leave everything else pretty much the same.
The overhead of having the null values may outweigh the speed gain involved in dictionary lookup, but I try to never use List. ... But that's just me.
|
|
|
|
|