如何声明嵌套枚举?

时间:2009-06-11 12:18:45

标签: c# enums

我想声明一个嵌套的枚举,如:

\\pseudocode
public enum Animal
{
  dog = 0,
  cat = 1
}

private enum dog
{
   bulldog = 0,
   greyhound = 1,
   husky = 3
}

private enum cat
{
   persian = 0,
   siamese = 1,
   burmese = 2
}

Animal patient1 = Animal.dog.husky;

可以吗?

12 个答案:

答案 0 :(得分:40)

我一直在寻找类似的方法来为日志记录系统创建轻量级的分层通道ID。我不太确定这是值得的,但我把它放在一起很开心,我在这个过程中学到了关于操作符重载和蜥蜴的新内容。

我已经建立了一种支持这种表示法的机制:

public static class Animal
{
    public static readonly ID dog = 1;
    public static class dogs
    {
        public static readonly ID bulldog = dog[0];
        public static readonly ID greyhound = dog[1];
        public static readonly ID husky = dog[3];
    }

    public static readonly ID cat = 2;
    public static class cats
    {
        public static readonly ID persian = cat[0];
        public static readonly ID siamese = cat[1];
        public static readonly ID burmese = cat[2];
    }

    public static readonly ID reptile = 3;
    public static class reptiles
    {
        public static readonly ID snake = reptile[0];
        public static class snakes
        {
            public static readonly ID adder = snake[0];
            public static readonly ID boa = snake[1];
            public static readonly ID cobra = snake[2];
        }

        public static readonly ID lizard = reptile[1];
        public static class lizards
        {
            public static readonly ID gecko = lizard[0];
            public static readonly ID komodo = lizard[1];
            public static readonly ID iguana = lizard[2];
            public static readonly ID chameleon = lizard[3];
        }
    }
}

你可以这样使用:

void Animalize()
{
    ID rover = Animal.dogs.bulldog;
    ID rhoda = Animal.dogs.greyhound;
    ID rafter = Animal.dogs.greyhound;

    ID felix = Animal.cats.persian;
    ID zorro = Animal.cats.burmese;

    ID rango = Animal.reptiles.lizards.chameleon;

    if (rover.isa(Animal.dog))
        Console.WriteLine("rover is a dog");
    else
        Console.WriteLine("rover is not a dog?!");

    if (rover == rhoda)
        Console.WriteLine("rover and rhoda are the same");

    if (rover.super == rhoda.super)
        Console.WriteLine("rover and rhoda are related");

    if (rhoda == rafter)
        Console.WriteLine("rhoda and rafter are the same");

    if (felix.isa(zorro))
        Console.WriteLine("er, wut?");

    if (rango.isa(Animal.reptile))
        Console.WriteLine("rango is a reptile");

    Console.WriteLine("rango is an {0}", rango.ToString<Animal>());
}

该代码编译并生成以下输出:

rover is a dog
rover and rhoda are related
rhoda and rafter are the same
rango is a reptile
rango is an Animal.reptiles.lizards.chameleon

这是使其有效的ID结构:

public struct ID
{
    public static ID none;

    public ID this[int childID]
    {
        get { return new ID((mID << 8) | (uint)childID); }
    }

    public ID super
    {
        get { return new ID(mID >> 8); }
    }

    public bool isa(ID super)
    {
        return (this != none) && ((this.super == super) || this.super.isa(super));
    }

    public static implicit operator ID(int id)
    {
        if (id == 0)
        {
            throw new System.InvalidCastException("top level id cannot be 0");
        }
        return new ID((uint)id);
    }

    public static bool operator ==(ID a, ID b)
    {
        return a.mID == b.mID;
    }

    public static bool operator !=(ID a, ID b)
    {
        return a.mID != b.mID;
    }

    public override bool Equals(object obj)
    {
        if (obj is ID)
            return ((ID)obj).mID == mID;
        else
            return false;
    }

    public override int GetHashCode()
    {
        return (int)mID;
    }

    private ID(uint id)
    {
        mID = id;
    }

    private readonly uint mID;
}

这可以使用:

  • 32位uint作为基础类型
  • 多个小数字填充到一个带位移的整数(你得到最多四级嵌套ID,每个级别有256个条目 - 你可以转换为ulong以获得更多级别或每个级别更多位)
  • ID 0作为所有ID的特殊根(可能ID.none应该被称为ID.root,任何id.isa(ID.root)都应该为true)
  • implicit type conversion将int转换为ID
  • indexer链接到ID
  • overloaded equality operators支持比较

到目前为止,一切都非常有效,但我不得不求助于ToString的反射和递归,所以我在extension method中封锁了它,如下所示:

using System;
using System.Reflection;

public static class IDExtensions
{
    public static string ToString<T>(this ID id)
    {
        return ToString(id, typeof(T));
    }

    public static string ToString(this ID id, Type type)
    {
        foreach (var field in type.GetFields(BindingFlags.GetField | BindingFlags.Public | BindingFlags.Static))
        {
            if ((field.FieldType == typeof(ID)) && id.Equals(field.GetValue(null)))
            {
                return string.Format("{0}.{1}", type.ToString().Replace('+', '.'), field.Name);
            }
        }

        foreach (var nestedType in type.GetNestedTypes())
        {
            string asNestedType = ToString(id, nestedType);
            if (asNestedType != null)
            {
                return asNestedType;
            }
        }

        return null;
    }
}

请注意,为了使其工作,Animal不再是静态类,因为static classes can't be used as type parameters,所以我用私有构造函数密封了它:

public /*static*/ sealed class Animal
{
    // Or else: error CS0718: 'Animal': static types cannot be used as type arguments
    private Animal()
    {
    }
    ....

唷!谢谢阅读。 : - )

答案 1 :(得分:16)

我可能会使用枚举位字段和扩展方法的组合来实现这一点。例如:

public enum Animal
{
   None = 0x00000000,
   AnimalTypeMask = 0xFFFF0000,
   Dog = 0x00010000,
   Cat = 0x00020000,
   Alsation = Dog | 0x00000001,
   Greyhound = Dog | 0x00000002,
   Siamese = Cat | 0x00000001
}

public static class AnimalExtensions
{
  public bool IsAKindOf(this Animal animal, Animal type)
  {
    return (((int)animal) & AnimalTypeMask) == (int)type);
  }
}

<强>更新
在.NET 4中,您可以使用Enum.HasFlag方法而不是滚动自己的扩展名。

答案 2 :(得分:11)

您可以使用此方法获得您想要的内容

public static class Animal {
    public enum Dog {
        BullDog,
        GreyHound,
        Huskey
    }

    public enum Cat {
        Tabby,
        Bombbay
    }
}

答案 3 :(得分:9)

简单地说,不,它不能。

我建议您定义Animal枚举中的所有值。你有什么理由想要这个特殊的结构吗?

答案 4 :(得分:7)

这是一个老问题,但我最近想知道这样的事情是否可行。似乎在C#中没有像枚举的继承那样,创建这样的东西的唯一方法就是自定义类,如yoyo的回答。问题是它们并不是真正的枚举(例如,不能在switch语句中使用),嵌套代码的性质使得难以快速阅读和理解。

我发现获得类似行为的最简单方法是使用单个扁平枚举,并使用包含关系(继承)的属性修饰枚举。这使得阅读和理解代码变得更加容易:

class AnimalAttribute : Attribute {}
class DogAttribute : AnimalAttribute {}
class CatAttribute : AnimalAttribute {}
class ReptileAttribute : AnimalAttribute {}
class SnakeAttribute : ReptileAttribute {}
class LizardAttribute : ReptileAttribute {}

enum Animal
{
    [Dog] bulldog,
    [Dog] greyhound,
    [Dog] husky,

    [Cat] persian,
    [Cat] siamese,
    [Cat] burmese,

    [Snake] adder,
    [Snake] boa,
    [Snake] cobra,

    [Lizard] gecko,
    [Lizard] komodo,
    [Lizard] iguana,
    [Lizard] chameleon
}

现在枚举可以像普通枚举一样使用,我们可以通过一些简单的扩展方法来检查它们之间的关系:

static class Animals
{

    public static Type AnimalType(this Enum value )
    {
        var member = value.GetType().GetMember(value.ToString()).FirstOrDefault();

        // this assumes a single animal attribute            
        return member == null ? null :
            member.GetCustomAttributes()
                .Where(at => at is AnimalAttribute)
                .Cast<AnimalAttribute>().FirstOrDefault().GetType();
    }

    public static bool IsCat(this Enum value) { return value.HasAttribute<CatAttribute>(); }

    public static bool IsDog(this Enum value) { return value.HasAttribute<DogAttribute>(); }

    public static bool IsAnimal(this Enum value) { return value.HasAttribute<AnimalAttribute>(); }

    public static bool IsReptile(this Enum value) { return value.HasAttribute<ReptileAttribute>(); }

    public static bool IsSnake(this Enum value) { return value.HasAttribute<SnakeAttribute>(); }

    public static bool IsLizard(this Enum value) { return value.HasAttribute<LizardAttribute>(); }

    public static bool HasAttribute<T>(this Enum value)
    {
        var member = value.GetType().GetMember(value.ToString()).FirstOrDefault();
        return member != null && Attribute.IsDefined(member, typeof(T));
    }

    public static string ToString<T>(this Animal value) where T : AnimalAttribute
    {
        var type = value.AnimalType();
        var s = "";
        while( type != null && !(type == typeof(Object)) )
        {
            s = type.Name.Replace("Attribute","") + "."+s;
            type = type.BaseType;
        }

        return s.Trim('.');
    }

}

测试类似于yoyos:

void Main()
{
    Animal rover  = Animal.bulldog;
    Animal rhoda = Animal.greyhound;
    Animal rafter = Animal.greyhound;

    Animal felix = Animal.persian;
    Animal zorrow = Animal.burmese;

    Animal rango = Animal.chameleon;

    if( rover.IsDog() )
        Console.WriteLine("rover is a dog");
    else
        Console.WriteLine("rover is not a dog?!");

    if( rover == rhoda )
        Console.WriteLine("rover and rhonda are the same type");

    if( rover.AnimalType() == rhoda.AnimalType() )
        Console.WriteLine("rover and rhonda are related");

    if( rhoda == rafter )
        Console.WriteLine("rhonda and rafter are the same type");

    if( rango.IsReptile() )
        Console.WriteLine("rango is a reptile");


    Console.WriteLine(rover.ToString<AnimalAttribute>());
}

唯一缺少的是嵌套类的点访问语法,但是如果你不编写性能关键代码,你可以用动态来实现类似的东西:

public static dynamic dogs
{
    get {
    var eo = new ExpandoObject() as IDictionary<string,object>;
    foreach( var value in Enum.GetValues(typeof(Animal)).Cast<Animal>().Where(a => a.IsDog()))
        eo[value.ToString()] = value;

    return eo;
    }
}

public static dynamic cats
{
    get {
    var eo = new ExpandoObject() as IDictionary<string,object>;
    foreach( var value in Enum.GetValues(typeof(Animal)).Cast<Animal>().Where(a => a.IsCat()))
        eo[value.ToString()] = value;

    return eo;
    }
}

添加这些扩展方法允许您访问具有特定属性的枚举,因此您可以将变量设置为:

Animal rhoda = Animals.dogs.greyhound;
Animal felix = Animals.cats.persian;

答案 5 :(得分:3)

我不认为它是这样的。

枚举应该是一组简单的并行值。

您可能想要表达与继承的关系。

答案 6 :(得分:2)

public class Animal
{
    public Animal(string name = "")
    {
        Name = name;
        Perform = Performs.Nothing;
    }

    public enum Performs
    {
        Nothing,
        Sleep,
        Eat,
        Dring,
        Moan,
        Flee,
        Search,
        WhatEver
    }

    public string Name { get; set; }

    public Performs Perform { get; set; }
}

public class Cat : Animal
{
    public Cat(Types type, string name) 
        : base (name)
    {
        Type = type;
    }

    public enum Types
    {
        Siamese,
        Bengal,
        Bombay,
        WhatEver
    }

    public Types Type { get; private set; }
}

public class Dog : Animal
{
    public Dog(Types type, string name)
        : base(name)
    {
        Type = type;
    }

    public enum Types
    {
        Greyhound,
        Alsation,
        WhatEver
    }

    public Types Type { get; private set; }
}

答案 7 :(得分:1)

见这些问题:
Getting static field values of a type using reflection
Storing string values as constants in the same manner as Enum

问题包括构建一个基本的字符串枚举,但我使用ICustomEnum<T>界面来实现我的答案,这可能会在这种情况下帮助你。

答案 8 :(得分:1)

此解决方案返回int,而不返回type。但是我这样使用它:

public static class Animals
{
    public static class Vertebrates
    {
        public static class Mammals
        {
            public enum Dogs
            {
                BullDog = 0,
                Greyhound = 1
            }
            public enum Cats
            {
                persian = 0,
                Greyhound = 1
            }
        }

        public static class Birds
        {
            public enum FirstType
            {
                FirstType0 = 0,
                FirstType1 = 1
            }
            public enum SecondType
            {
                SecondType0 = 0,
                SecondType1 = 1
            }
        }
    }
}

用法:

int i = (int)Animals.Vertebrates.Mammals.Dogs.BullDog; 
int j = (int)Animals.Vertebrates.Birds.FirstType.FirstType0;

答案 9 :(得分:0)

也许这就足够了?

class A
{
  public const int Foo = 0;
  public const int Bar = 1;
}

class B : A
{
  public const int Baz = 2;
}

答案 10 :(得分:0)

public enum Animal
{
    CAT_type1= AnimalGroup.CAT,
    CAT_type2 = AnimalGroup.CAT,

    DOG_type1 = AnimalGroup.DOG,
}

public enum AnimalGroup
{
    CAT,
    DOG
}

public static class AnimalExtensions
{
    public static bool isGroup(this Animal animal,AnimalGroup groupNumber)
    {
        if ((AnimalGroup)animal == groupNumber)
            return true;
        return false;
    }
}

答案 11 :(得分:0)

这是我的解决方案/解决方法:

public static class Categories
{
    public const string Outlink = "Outlink";
    public const string Login = "Login";
}

public enum Action
{
    /// <summary>
    /// Outlink is a anchor tag pointing to an external host
    /// </summary>
    [Action(Categories.Outlink, "Click")]
    OutlinkClick,
    [Action(Categories.Outlink, "ClickBlocked")]
    OutlinkClickBlocked,

    /// <summary>
    /// User account events
    /// </summary>
    [Action(Categories.Login, "Succeeded")]
    LoginSucceeded,
    [Action(Categories.Login, "Failed")]
    LoginFailed
}

public class ActionAttribute : Attribute
{
    public string Category { get; private set; }
    public string Action { get; private set; }
    public ActionAttribute(string category, string action)
    {
        Category = category;
        Action = action;
    }
}