开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜