如何声明嵌套枚举?

cal*_*sto 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;
Run Code Online (Sandbox Code Playgroud)

可以吗?

yoy*_*oyo 42

我正在寻找类似的东西来为日志系统创建轻量级的分层通道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];
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以这样使用:

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>());
}
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

这是使它工作的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;
}
Run Code Online (Sandbox Code Playgroud)

这利用了:

  • 一个32位的uint作为底层类型
  • 多个小数字填充到一个带位移的整数(你得到最多四级嵌套ID,每个级别有256个条目 - 你可以转换为ulong以获得更多级别或每个级别更多位)
  • ID 0作为所有ID的特殊根(可能ID.none应该被称为ID.root,任何id.isa(ID.root)都应该为true)
  • 隐式类型转换将int转换为ID
  • 一个将ID链接在一起的索引器
  • 重载相等运算符以支持比较

到目前为止,一切都非常有效,但我不得不求助于ToString的反射和递归,所以我在扩展方法中将其封锁,如下所示:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,为了使其工作,Animal不再是静态类,因为静态类不能用作类型参数,所以我用私有构造函数密封它:

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

唷!谢谢阅读.:-)

  • 哇,五年后!尽管此后我两次换工作,但我确实记得您当时的解决方案就是我想要的。您的解决方案将显示说明,解决方案,实现和结果:太好了!感谢您为解决这个问题而付出的努力,因为这个问题似乎访问了很多人。 (2认同)
  • 谢谢@callisto,我不一定希望这是"正确的"答案(不确定是否真的有),但正如我所说,我很乐意这样做,我也许可以利用它.(现在,不应该有一个徽章,以回答5岁的问题标记正确吗?;-) (2认同)
  • 确实应该有。“平反”徽章。 (2认同)

Jef*_*tes 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);
  }
}
Run Code Online (Sandbox Code Playgroud)

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

  • 假设有些品种对某些物种无效,上面的枚举反对建议当某些值的组合从[Microsoft的标志枚举设计指南]无效时创建标志枚举(http://msdn.microsoft .COM/EN-US /库/ vstudio/ms229062.aspx). (3认同)
  • 我认为这个解决方案在设计上很糟糕(ANTI OOD),可读性很差,而且维护也很糟糕。 (2认同)
  • 枚举类型与OO几乎没有关系; 它们只是类型安全的常量. (2认同)

Nic*_*rdi 11

您可以使用此方法获得所需的内容

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

    public enum Cat {
        Tabby,
        Bombbay
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是一个公平的建议,尽管我仍然不是我会选择这样做的.它为您提供了正确的语法,但不是必要的继承.就CLR而言,狗与猫是完全无关的.简单地定义单个动物枚举中的所有值表明它们在某种形式(即所有动物)中是同质的.考虑一下:你如何通过Dog*或*Cat作为参数? (8认同)
  • 未能回答 OP 的输入请求:`Animalpatient1 = Animal.dog.husky;` (3认同)

Nol*_*rin 9

简单地说,不,它不能.

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

  • 我有这样的想法,但是想把主要的枚举细分为潜在的不同群体. (3认同)

bj0*_*bj0 8

这是一个老问题,但我最近想知道这样的事情是否可行.似乎在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
}
Run Code Online (Sandbox Code Playgroud)

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

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('.');
    }

}
Run Code Online (Sandbox Code Playgroud)

测试类似于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>());
}
Run Code Online (Sandbox Code Playgroud)

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

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Animal rhoda = Animals.dogs.greyhound;
Animal felix = Animals.cats.persian;
Run Code Online (Sandbox Code Playgroud)