C# : Building java-style enums with inheritance
I am looking to build a java-style enum pattern for C# that also supports inheritance. I'm having trouble with yield return. Specifically, returning the BaseEnum's Values from the ChildEnum's Values property.
public class BaseEnum {
public static readonly BaseEnum A = new BaseEnum("A");
public static readonly BaseEnum B = new BaseEnum("B");
public static readonly BaseEnum C = new BaseEnum("C");
public static IEnumerable<BaseEnum> Values {
get {
yield return A;
yield return B;
yield return C;
}
}
public readonly String Name;
protected BaseEnum(String name) {
this.Name = name;
}
public static void TestMain() {
Console.WriteLine("Ba开发者_Go百科seEnum, should print (A,B,C):");
foreach(BaseEnum e in BaseEnum.Values) {
Console.WriteLine(e.Name);
}
Console.WriteLine("BaseEnum in ChildEnum, should print (A,B,C,D,E):");
foreach(BaseEnum e in ChildEnum.Values) {
Console.WriteLine(e.Name);
}
Console.WriteLine("ChildEnum in ChildEnum, should print (D,E):");
foreach(ChildEnum e in ChildEnum.Values) {
Console.WriteLine(e.Name);
}
}
}
class ChildEnum : BaseEnum {
public static readonly ChildEnum D = new ChildEnum("D");
public static readonly ChildEnum E = new ChildEnum("E");
new public static IEnumerable<BaseEnum> Values {
get {
// yield return BaseEnum.Values; // This is what I want to do. It gives the error message below:
// Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<Abra.Workshop.EnumABC>'
// to 'Abra.Workshop.EnumABC'. An explicit conversion exists (are you missing a cast?)
yield return D;
yield return E;
}
}
public ChildEnum(string name)
: base(name) {
}
}
/* Output!
BaseEnum, should print (A,B,C):
A
B
C
BaseEnum in ChildEnum, should print (A,B,C,D,E):
D
E
ChildEnum in ChildEnum, should print (D,E):
D
E
*/
Use
foreach (var b in BaseEnum.Values)
yield return b;
You need to unwrap BaseEnum.Values into individual elements.
Or replace it with a LINQ expression:-
return BaseEnum.Values.Concat(new[]{ D, E });
class Program
{
static void Main(string[] args)
{
Console.WriteLine("BaseEnum, should print (A,B,C):");
foreach (BaseEnum e in BaseEnum.Values)
{
Console.WriteLine(e.Name);
}
Console.WriteLine("BaseEnum in ChildEnum, should print (A,B,C,D,E):");
foreach (BaseEnum e in ChildEnum.Values)
{
Console.WriteLine(e.Name);
}
Console.WriteLine("ChildEnum in ChildEnum, should print (D,E):");
foreach (ChildEnum e in ChildEnum.Values.Where(d => d.GetType() == typeof(ChildEnum)))
{
Console.WriteLine(e.Name);
}
}
}
public class BaseEnum
{
public static readonly BaseEnum A = new BaseEnum("A");
public static readonly BaseEnum B = new BaseEnum("B");
public static readonly BaseEnum C = new BaseEnum("C");
public static IEnumerable<BaseEnum> Values
{
get
{
yield return A;
yield return B;
yield return C;
}
}
public readonly String Name;
protected BaseEnum(String name)
{
this.Name = name;
}
}
public class ChildEnum : BaseEnum
{
public static readonly ChildEnum D = new ChildEnum("D");
public static readonly ChildEnum E = new ChildEnum("E");
new public static IEnumerable<BaseEnum> Values
{
get
{
foreach (var baseEnum in BaseEnum.Values)
yield return baseEnum;
yield return D;
yield return E;
}
}
public ChildEnum(string name)
: base(name)
{
}
}
Here's a version that does away with the need to have the nasty new public static
enumerator.
It does change the inheritance structure to make it work though. Thought I'd put it forward thought as an alternative.
public class BaseEnum<E> where E : BaseEnum<E>, new()
{
public static readonly E A = new E() { Name = "A" };
public static readonly E B = new E() { Name = "B" };
public static readonly E C = new E() { Name = "C" };
public string Name { get; protected set; }
protected static IEnumerable<E> InternalValues
{
get
{
yield return A;
yield return B;
yield return C;
}
}
}
public class BaseEnum : BaseEnum<BaseEnum>
{
public static IEnumerable<BaseEnum> Values
{
get { foreach (var x in InternalValues) yield return x; }
}
}
public class ChildEnum : BaseEnum<ChildEnum>
{
public static readonly ChildEnum D = new ChildEnum() { Name = "D" };
public static readonly ChildEnum E = new ChildEnum() { Name = "E" };
public static IEnumerable<ChildEnum> Values
{
get
{
foreach (var x in InternalValues) yield return x;
yield return D;
yield return E;
}
}
}
It does also require a default public constructor, but thanks to the protected setter on the Name
property only code running in the inheritance hierarchy can set the name, so if an instance appeared without a name you know that someone is doing something wrong.
精彩评论