我想声明一个嵌套的枚举,如:
\\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;
可以吗?
答案 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;
}
这可以使用:
到目前为止,一切都非常有效,但我不得不求助于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;
}
}