碧水寒潭

追寻平淡的幸福:和喜欢的人在一起,做自己喜欢的事……
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

自定义属性和枚举

Posted on 2010-08-09 20:55  碧水寒潭  阅读(600)  评论(0编辑  收藏  举报

  很早以前就知道 .Net 中有反射这种技术的存在,而且我早先实现的数据库组件就是使用的反射。不过,以前不知道有自定义属性这种东西,所以,也没有把反射看作一种重要的技术来看待,而现在,却发现,在有了自定义属性的支持下,反射真的可以完成很多任务,可以使得本来繁杂的操作变得简单。

  我前一段写的《使用反射分发 switch 操作》就是一个比较典型的例子,如果没有自定义属性,那个例子的程序就会无法完成一些特殊的操作,而且会隐含很多的出错的可能性,而使用自定义属性后,这一切都在控制中,反而使得使用它的范围加宽了很多,而令其使用上有了更多的自由。

  前两天,写了一个关于 enum 的自定义属性 :

代码
public class ShowStringAttribute : Attribute
{
private string _ShowString;

public string ShowString
{
get { return _ShowString; }
}

public ShowStringAttribute(string ShowString)
{
_ShowString
= ShowString;
}
}

  虽然现在,.net 中关于 enum 的 ToString 操作会输出这个 enum 的名字,不过,有很多情况下,我们希望使用不同的语言来显示它,或者,我们想要显示的字符串中有非法字符(比如空格,对于变量来讲,是非法字符,而对于显示来说,就是一个普通的要求了),而这个属性,帮助我们显示 enum 所定义的字符串,不过,很可惜的,虽然任何的 enum 都是从 System.Enum 派生的,不过,我们没有办法改写它的 ToString 函数,所以,需要一个 Helper 类来帮助它的输出:

代码
public class EnumStringHelper
{
public static string ToString(object o)
{
Type t
= o.GetType();
string s = o.ToString();
ShowStringAttribute[] os
= (ShowStringAttribute[])t.GetField(s).GetCustomAttributes(typeof(ShowStringAttribute), false);
if ( os != null && os.Length == 1 )
{
return os[0].ShowString;
}
return s;
}
}

 

使用上,类似这样:

代码
public enum EnumTest
{
[ShowString(
"测试")]
test,

[ShowString(
"运行")]
run,

exit
}

class Program
{
[STAThread]
static void Main(string[] args)
{
Console.WriteLine(
"-------------------------------------------");
Console.WriteLine( EnumStringHelper.ToString( EnumTest.test ) );
Console.WriteLine( EnumStringHelper.ToString( EnumTest.run ) );
Console.WriteLine( EnumStringHelper.ToString( EnumTest.exit ) );
Console.WriteLine(
"-------------------------------------------");
Console.ReadLine();
}
}

  当然,其中的 EnumStringHelper.ToString 其实蛮丑陋的,这促使我开始思考 enum 的实现机制问题,虽然仍然没有解决重写 enum 的 ToString 函数的目的,不过,却让我觉得这也是一个比较有意思的问题。

  在 C++ 和 Java 中,enum 都只是一个 int 的变形而已,所以,其实是无法实现所谓的“强类型检查”的,那么,在 .net 中究竟是使用什么方法实现的呢?经过一些实践和思考,我觉得,其实,当我们在 .net 中定一个 enum 的时候,当然,首先是从 System.Enum 中继承,而另外一点比较隐晦的是,每一个被定义出来的枚举值,都是这个类的一个静态只读的实例!下面的代码展示了这一点,而这种方法也同时允许我们在 C++ 或者 Java 中实现自己的可以进行“强类型检查”的枚举:

代码
public abstract class EnumBase
{
private int _value;

protected EnumBase(int value)
{
_value
= value;
}

public override string ToString()
{
Type t
= this.GetType();
FieldInfo[] fis
= t.GetFields();
foreach ( FieldInfo fi in fis )
{
EnumBase eb
= (EnumBase)fi.GetValue(this);
if ( eb._value == this._value )
{
return fi.Name;
}
}
throw new NotImplementedException();
}
}

public class MyEnum : EnumBase
{
private MyEnum(int v) : base(v) {}

public static readonly MyEnum test = new MyEnum(0);
public static readonly MyEnum run = new MyEnum(1);
public static readonly MyEnum exit = new MyEnum(2);
}

  当然,我还希望,刚才为 enum 定义的自定义属性可以用于我自己实现的 enum,既然我认为它的实现机制和 enum 是一样的,当然,使用那个自定义属性也应该很轻松,而且,这一次,我们实现的是一个普通的类,所以,是可以重写它的 ToString 函数的:

代码
public abstract class EnumBase
{
private int _value;

protected EnumBase(int value)
{
_value
= value;
}

public override string ToString()
{
Type t
= this.GetType();
FieldInfo[] fis
= t.GetFields();
foreach ( FieldInfo fi in fis )
{
EnumBase eb
= (EnumBase)fi.GetValue(this);
if ( eb._value == this._value )
{
return fi.Name;
}
}
throw new NotImplementedException();
}

}

public abstract class NamedEnumBase : EnumBase
{
protected NamedEnumBase(int v) : base(v) {}

public override string ToString()
{
Type t
= this.GetType();
string s = base.ToString();
ShowStringAttribute[] os
= (ShowStringAttribute[])t.GetField(s).GetCustomAttributes(typeof(ShowStringAttribute), false);
if ( os != null && os.Length == 1 )
{
return os[0].ShowString;
}
return s;
}
}

public class MyEnum : NamedEnumBase
{
private MyEnum(int v) : base(v) {}

[ShowString(
"测试")]
public static readonly MyEnum test = new MyEnum(0);

[ShowString(
"运行")]
public static readonly MyEnum run = new MyEnum(1);

public static readonly MyEnum exit = new MyEnum(2);
}

class Program
{
[STAThread]
static void Main(string[] args)
{
Console.WriteLine(
"-------------------------------------------");
Console.WriteLine( MyEnum.test.ToString() );
Console.WriteLine( MyEnum.run.ToString() );
Console.WriteLine( MyEnum.exit.ToString() );
Console.WriteLine(
"-------------------------------------------");
Console.ReadLine();
}
}

  当然,.net 中的 enum 还实现了对于“位与”后的强类型检查,虽然不见得非常困难,不过还算是一个比较麻烦的问题,我就不实现了。 :)

 

转自<http://llf.hanzify.org/studio/article/show/351>