我花了一些时间来了解更多有关C#的信息,所以我决定研究自定义属性,并且在分配给枚举时我发现它们非常有用。
所以我为Enum编写了一些扩展方法,以便很容易地检索这些属性,例如:DemoEnum.Value1.GetAttribute<EnumNote>()
。
过了一段时间我觉得如果每个自定义属性都有对它所分配的枚举的引用,那将是一个不错的主意。我想是个不错的主意,所以我继续这样做:
首先,我为自定义属性编写了基类EnumAttribute
,当然,它继承了System.Attribute
类。这个基类只是第一个草图,我打算将它扩展为特别适合它将接收的每种类型的枚举,但到目前为止这已足够。
public class EnumAttribute : Attribute
{
public EnumInfo Enum { get; internal set; }
public EnumAttribute(Enum Enum)
{
this.Enum = new EnumInfo(Enum);
}
public class EnumInfo
{
private Enum _value;
private Type _type;
private FieldInfo _details;
public Enum Value { get { return _value; } }
public Type Type { get { return _type; } }
public FieldInfo Details { get { return _details; } }
public EnumInfo(Enum value)
{
_value = value;
_type = value.GetType();
_details = _type.GetField(System.Enum.GetName(_type, value));
}
}
}
现在每个自定义属性都必须从此类EnumAttribute
继承。例如,在像EnumNote
:
public class EnumNote : EnumAttribute
{
private string _note = string.Empty;
public string Note { get { return _note; } }
public EnumNote(Enum Enum, string Note)
: base(Enum)
{
_note = Note;
}
}
到目前为止一切都很好,Visual Studio代码分析和编译器都没有报告。
但是当我定义一个Enum时,例如:
public enum DemoEnum
{
[EnumNote(DemoEnum.Value1, "Some special note about Enum Value1.")]
Value1 = 1,
[EnumNote(DemoEnum.Value2, "Some other special note about Enum Value2.")]
Value2 = 2
}
当我尝试编译它时,VS会报告每个EnumNote
构造函数的第一个参数:
属性参数必须是常量表达式,typeof表达式 或属性参数类型的数组创建表达式。
基本上是说DemoEnum.Value1和DemoEnum.Value2不保持常量值。我是对的?
任何方式,这个错误让我感到困惑,因为这个Enum是硬编码的,正如你所看到的,编译器甚至不必为每个枚举赋值,因为我已经自己做过了。
这当然会带来一个问题,我缺少什么或误解,以及如何实现提供每个EnumNote
分配给枚举的枚举的目标?
谢谢。
更新
在重新审视之后,我理解为什么VS报告枚举不是一个常量表达式,因为我指的是:DemoEnum.Value1
在某一点上,他还没有完成DemoEnum
首先为Value1
提供定义。但是没有关于如何继续这个想法的想法。
更新2:
如果在编译后重构Enum,那么它可能不应该给出任何错误,除非它不能应用于枚举(不记得重构是否适用于枚举),但如果它可以是应用它可能会很麻烦,做得慢,对吗?
更新3:属性包含对分配给它的枚举的引用的原因。
实际上,这是一个非常简单的原因,假设以下情况。假设我有一组自定义属性,由于某种原因,我需要知道给定属性属于哪个枚举。
为什么不简单地在属性本身中引用给定的Enum,而不是编写新的代码来确定它?这是对内存消耗的微小妥协,而将来它可以节省宝贵的时间来运行任何所需的进程来确定每个属性的给定枚举(甚至只有一个)。
答案 0 :(得分:8)
属性参数必须是属性参数类型的常量表达式,typeof表达式或数组创建表达式。
基本上是说DemoEnum.Value1和DemoEnum.Value2没有保持恒定值。我是对的吗?
不完全。
实际问题似乎是使用Enum
。虽然它主要将 作为概念类层次结构的基类,但它似乎是用一点技巧来处理的。对于所有意图和目的,它是一个运行时伪类而不是真正的类。
属性在编译时处理。没有在运行时使用属性装饰对象的机制。因此,引入一些运行时技巧来处理Enum
伪类似乎与属性的运行方式不兼容。
试试这个:
public class EnumTest : Attribute
{
public int Value;
public object Obj;
}
public enum DemoEnum
{
[EnumTest(Value = (int)DemoEnum.Value1, Obj = DemoEnum.Value1)]
Value1 = 1,
[EnumTest(Value = (int)DemoEnum.Value2)]
Value2 = 2
}
这编译很好(VS2010中的.NET 4.0),并且属性属性设置正确。我可以检查属性的Obj
属性,并获得我期望的那种数据。
<击>&LT;&意见GT;
那说......不太确定这会有多大用处。您必须拥有一个您正在装饰的值的实例,因此将值本身存储在属性中似乎有点多余。
&LT; /&意见GT; 击>
编辑:可能的解决方案......
让这个渗透了一下(我在睡着的时候做了一些最好的思考),我已经意识到,因为问题在于实际的参数类型(如{{的答案中所述) 3}})我们可以通过一些拳击来解决这个问题。
public class EnumTest : Attribute
{
public Enum enum;
public EnumTest(object e)
{
enum = e as Enum;
}
}
这样你就可以从一个对象初始化属性,C#和CLR看起来很舒服。您在使用Enum初始化时失去了固有的类型检查,但我相信它达到了既定目标。
答案 1 :(得分:1)
您无法在编译时值中传递属性构造函数非常量。它是对属性的限制。所以你只能
[EnumNote(1,"Some special note")]
Value1=1,
但是你的目的打破了在c#中混合面向方面编程的属性的想法。所以不要用属性来做。
关于属性和面向方面的编程,您可以阅读this article