C# 如何声明嵌套枚举?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/980766/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I declare a nested enum?
提问by callisto
I want to declare a nested enum like:
我想声明一个嵌套的枚举,如:
\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;
Can it be done?
可以做到吗?
采纳答案by yoyo
I was looking for something similar as a way to create lightweight, hierarchical channel ID's for a logging system. I'm not quite sure this was worth the effort, but I had fun putting it together, and I learned something new about operator overloading and lizards in the process.
我一直在寻找类似于为日志记录系统创建轻量级分层通道 ID 的方法。我不太确定这是否值得付出努力,但我把它放在一起很有趣,我在这个过程中学到了一些关于运算符重载和蜥蜴的新东西。
I've built a mechanism that supports this notation:
我已经建立了一个支持这种表示法的机制:
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];
}
}
}
And which you can use like so:
你可以像这样使用:
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>());
}
That code compiles and produces the following output:
该代码编译并产生以下输出:
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
Here's the ID struct that makes it work:
这是使其工作的 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;
}
This makes use of:
这利用了:
- a 32-bit uint as the underlying type
- multiple small numbers stuffed into an integer with bit shifts (you get maximum four levels of nested ID's with 256 entries at each level -- you could convert to ulong for more levels or more bits per level)
- ID 0 as the special root of all ID's (possibly ID.none should be called ID.root, and any id.isa(ID.root) should be true)
- implicit type conversionto convert an int into an ID
- an indexerto chain ID's together
- overloaded equality operatorsto support comparisons
- 一个 32 位 uint 作为基础类型
- 将多个小数字填充到带有位移的整数中(您最多可以获得四级嵌套 ID,每个级别有 256 个条目——您可以转换为 ulong 以获得更多级别或每级更多位)
- ID 0 作为所有 ID 的特殊根(可能 ID.none 应该称为 ID.root,并且任何 id.isa(ID.root) 都应该是 true)
- 将 int 转换为 ID 的隐式类型转换
- 将ID 链接在一起的索引器
- 重载相等运算符以支持比较
Up to now everything's pretty efficient, but I had to resort to reflection and recursion for ToString, so I cordoned it off in an extension method, as follows:
到目前为止,一切都非常有效,但我不得不对 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;
}
}
Note that for this to work Animal could no longer be a static class, because static classes can't be used as type parameters, so I made it sealed with a private constructor instead:
请注意,为此,Animal 不能再是静态类,因为静态类不能用作类型参数,所以我用私有构造函数对其进行了密封:
public /*static*/ sealed class Animal
{
// Or else: error CS0718: 'Animal': static types cannot be used as type arguments
private Animal()
{
}
....
Phew! Thanks for reading. :-)
呼!谢谢阅读。:-)
回答by Noldorin
Simply, no, it cannot.
简单地说,不,它不能。
I recommend that you define all of the values within the Animal
enum. Is there any reason why you want this particular structure?
我建议您定义Animal
枚举中的所有值。你有什么理由想要这个特殊的结构吗?
回答by lyxera
I don't think it works that way.
我不认为它是那样工作的。
Enumerations are supposed to be a simple set of parallel values.
枚举应该是一组简单的并行值。
You may want to express that relationship with inheritance.
您可能想用继承来表达这种关系。
回答by Nick Berardi
You can use this method to get what you want though
你可以使用这种方法来获得你想要的东西
public static class Animal {
public enum Dog {
BullDog,
GreyHound,
Huskey
}
public enum Cat {
Tabby,
Bombbay
}
}
回答by Joel Coehoorn
See these questions:
Getting static field values of a type using reflection
Storing string values as constants in the same manner as Enum
请参阅这些问题:
使用反射获取类型的静态字段值
以与枚举相同的方式将字符串值存储为常量
The questions cover building a basic string enum, but I implement my answers using an ICustomEnum<T>
interface that might help you in this situation.
这些问题包括构建一个基本的字符串枚举,但我使用ICustomEnum<T>
在这种情况下可能对您有所帮助的接口来实现我的答案。
回答by leppie
Perhaps this would suffice?
也许这就足够了?
class A
{
public const int Foo = 0;
public const int Bar = 1;
}
class B : A
{
public const int Baz = 2;
}
回答by Jeff Yates
I would probably use a combination of enumerated bit fields and extension methods to achieve this. For example:
我可能会使用枚举位域和扩展方法的组合来实现这一点。例如:
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);
}
}
Update
In .NET 4, you can use the Enum.HasFlag
method rather than roll your own extension.
更新
在 .NET 4 中,您可以使用该Enum.HasFlag
方法而不是滚动您自己的扩展。
回答by Jens-Axel Grünewald
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; }
}
回答by user2685321
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;
}
}
回答by bj0
This is an old question, but I recently wondered if something like this was possible. It seems that in C# there is nothing like inheritance for enums and the only way to create something like this would be custom classes like yoyo's answer. The problem is that they aren't really enums (can't be used in switch statements for instance), and the nature of the nested code makes it difficult to read and understand quickly.
这是一个老问题,但我最近想知道这样的事情是否可能。似乎在 C# 中没有像枚举的继承一样,创建这样的东西的唯一方法是像 yoyo 的答案这样的自定义类。问题是它们并不是真正的枚举(例如不能在 switch 语句中使用),并且嵌套代码的性质使其难以快速阅读和理解。
I found that the easiest way to get similar behavior was to use a single, flat enum and decorate the enums with Attributes that contained the relationships (inheritance). This makes for much easier to read and understand code:
我发现获得类似行为的最简单方法是使用单个平面枚举并使用包含关系(继承)的属性装饰枚举。这使得阅读和理解代码变得更加容易:
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
}
Now the enums can be used just like normal enums, and we can examine their relationships with a few simple extension methods:
现在枚举可以像普通枚举一样使用,我们可以通过一些简单的扩展方法检查它们的关系:
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('.');
}
}
Test similar to yoyos:
类似于 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>());
}
The only thing missing is the dot-access syntax of nested classes, but if you are not writing performance critical code you can achive something similar with dynamics:
唯一缺少的是嵌套类的点访问语法,但如果您不编写性能关键代码,您可以实现与动态类似的东西:
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;
}
}
Adding extension methods like these allows you to access enums with specific attributes, so you can set variables as:
添加像这样的扩展方法允许您访问具有特定属性的枚举,因此您可以将变量设置为:
Animal rhoda = Animals.dogs.greyhound;
Animal felix = Animals.cats.persian;