我如何声明一个嵌套的枚举?
我想要声明一个嵌套的枚举,如:
\\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;
可以这样做吗?
我一直在寻找类似的方法来为日志logging系统创build轻量级的通道ID。 我不太确定这是值得的,但是我很乐意把它放在一起,我在这个过程中学到了一些关于运营商超载和蜥蜴的新东西。
我已经构build了一个支持这种表示法的机制:
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; }
这使用了:
- 作为基础types的32位uint
- 多个小数字填充到一个整数位移(最多可以获得四级嵌套ID,每个级别有256个条目 – 您可以将每个级别转换为ulong以获得更多级别或更多位)
- ID 0作为所有ID的特殊根(可能ID.none应该被称为ID.root,并且任何id.isa(ID.root)应该为真)
- 隐式types转换将int转换为ID
- 一个索引器将ID链接在一起
- 超载的平等运营商支持比较
到目前为止,一切都非常高效,但我不得不求助于ToString的reflection和recursion,所以我用扩展方法封锁了它,如下所示:
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不再是一个静态类,因为静态类不能用作types参数 ,所以我用一个私有构造函数来封装:
public /*static*/ sealed class Animal { // Or else: error CS0718: 'Animal': static types cannot be used as type arguments private Animal() { } ....
唷! 谢谢阅读。 🙂
我可能会使用枚举位域和扩展方法的组合来实现这一点。 例如:
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
方法而不是滚动您自己的扩展。
简而言之,不,它不能。
我build议您定义Animal
枚举中的所有值。 你有什么理由要这个特定的结构?
你可以使用这个方法来得到你想要的
public static class Animal { public enum Dog { BullDog, GreyHound, Huskey } public enum Cat { Tabby, Bombbay } }
这是一个古老的问题,但我最近想知道这样的事情是否可能。 看起来,在C#中没有什么像枚举inheritance,创build类似这样的唯一方法就是像yoyo的答案一样的自定义类。 问题在于,它们并不是真正的枚举(不能用于switch语句中),嵌套代码的性质使得很难快速阅读和理解。
我发现获得类似行为最简单的方法是使用一个单一的枚举,并用包含关系(inheritance)的属性装饰枚举。 这使得阅读和理解代码变得更容易:
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('.'); } }
testing类似于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>()); }
唯一缺less的是嵌套类的点访问语法,但是如果你不写性能关键代码,你可以通过dynamic获得类似的东西:
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; } }
添加这样的扩展方法允许您访问具有特定属性的枚举,因此您可以将variables设置为:
Animal rhoda = Animals.dogs.greyhound; Animal felix = Animals.cats.persian;
我不认为这样做。
枚举应该是一组简单的并行值。
你可能想用inheritance来expression这种关系。
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; } }
看到这些问题:
使用reflection获取types的静态字段值
以与Enum相同的方式将string值存储为常量
这些问题包括build立一个基本的string枚举,但我实现我的答案使用ICustomEnum<T>
接口,可能会帮助你在这种情况下。
也许这就够了?
class A { public const int Foo = 0; public const int Bar = 1; } class B : A { public const int Baz = 2; }
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; } }