我不知道其他人怎么样,但是我就是喜欢枚举类型。而且,我也喜欢和枚举结合在一起的Flags Attribute(不知道翻成什么好)。本文将探讨配合着扩展方法如何使用二者来使你的代码更紧凑、易懂。
如果你从来没有将二者结合起来过,请不要错过。考虑下面的代码:
class
User {
bool CanDelete;
bool CanRead;
bool CanWrite;
bool CanModify;
bool CanCreate;
}
bool CanDelete;
bool CanRead;
bool CanWrite;
bool CanModify;
bool CanCreate;
}
好的,这个看起来没什么大不了的,甚至还可能是几行额外的代码。如果能够将所有的那些权限都组合到一个值里,那就再好不过了。我们可以用一个带有FlagAttribute的枚举类型来解决这个问题。
enum
PermissionTypes :
int
{
None = 0 ,
Read = 1 ,
Write = 2 ,
Modify = 4 ,
Delete = 8
Create = 16 ,
All = Read | Write | Modify | Delete | Create
}
// and the class from before
class User {
PermissionTypes Permissions = PermissionTypes.None;
}
None = 0 ,
Read = 1 ,
Write = 2 ,
Modify = 4 ,
Delete = 8
Create = 16 ,
All = Read | Write | Modify | Delete | Create
}
// and the class from before
class User {
PermissionTypes Permissions = PermissionTypes.None;
}
上面这个枚举的优点就在于我们可以将多个值分配到相同的属性。不仅如此,我们还可以通过比较,对已存值进行检测。
//
create a new user
User admin = new User();
admin.Permissions = PermissionTypes.Read
| PermissionTypes.Write
| PermissionTypes.Delete;
// check for permissions
bool canRead = ((PermissionTypes.Read & admin.Permissions) == PermissionTypes.Read);
bool canWrite = ((PermissionTypes.Write & admin.Permissions) == PermissionTypes.Write);
bool canCreate = ((PermissionTypes.Create & admin.Permissions) == PermissionTypes.Create);
// and the results
Console.WriteLine(canRead); // true
Console.WriteLine(canWrite); // true
Console.WriteLine(canCreate); // false
User admin = new User();
admin.Permissions = PermissionTypes.Read
| PermissionTypes.Write
| PermissionTypes.Delete;
// check for permissions
bool canRead = ((PermissionTypes.Read & admin.Permissions) == PermissionTypes.Read);
bool canWrite = ((PermissionTypes.Write & admin.Permissions) == PermissionTypes.Write);
bool canCreate = ((PermissionTypes.Create & admin.Permissions) == PermissionTypes.Create);
// and the results
Console.WriteLine(canRead); // true
Console.WriteLine(canWrite); // true
Console.WriteLine(canCreate); // false
现在代码看起来非常简短易读。但是,当你要检测一个值的时候,每次都需要把代码重写一遍。那还不是很糟,但是我真的不想经常的去敲代码。当然,你可以写一个单独的函数来做这个比较,但是相对而言,我们还更很好的方法。
发挥扩展方法的优势
因为枚举类型并不是一个类,所以,并不能对枚举类型进行方法扩展。然而,在System.Enum这个类里,是可以将方法扩展的。添加进这个类里的方法,会出现在所有的枚举类型中。
这里有一个例子:
//
full class included at the end of the post
public static class EnumerationExtensions {
// checks to see if an enumerated value contains a type
public static bool Has < T > ( this System.Enum type, T value) {
try {
return ((( int )( object )type &
( int )( object )value) == ( int )( object )value);
}
catch {
return false ;
}
}
}
public static class EnumerationExtensions {
// checks to see if an enumerated value contains a type
public static bool Has < T > ( this System.Enum type, T value) {
try {
return ((( int )( object )type &
( int )( object )value) == ( int )( object )value);
}
catch {
return false ;
}
}
}
现在,这段代码假设枚举类型可以转化为整型。在做比较之前可以做一些类型检测,但是,这个例子的初衷是要保持代码简短。
那么,如何来使用这个扩展呢?
//
start with a value
PermissionTypes permissions = PermissionTypes.Read | PermissionTypes.Write;
// then check for the values
bool canRead = permissions.Has(PermissionTypes.Read); // true
bool canWrite = permissions.Has(PermissionTypes.Write); // true
bool canDelete = permissions.Has(PermissionTypes.Delete); // false
PermissionTypes permissions = PermissionTypes.Read | PermissionTypes.Write;
// then check for the values
bool canRead = permissions.Has(PermissionTypes.Read); // true
bool canWrite = permissions.Has(PermissionTypes.Write); // true
bool canDelete = permissions.Has(PermissionTypes.Delete); // false
现在,代码变得更加易读了。甚至,可以注意到,这个扩展有一个通用参数,在使用这个方法之前我们不必提供参数类型,因为方法本身能够根据参数推断出来。
同时,也要记住,System.Enum并不是唯一一个可以做这个的类,也有一些其他的类(如:System.Array),你可以在这些类中添加自己的扩展方法,你会有意想不到的收获。
正如我先前提到的,这段代码不可能涵盖所有情况,你需要根据需要修改它。例如,如果你使用long,uint,ulong,这段代码就不能胜任了。
你可能会感到奇怪,为什么我们在将一个参数转化为int类型之前,先要转换成object类型呢?当你和通用参数打交道的时候,你并不能立刻将其转换为值类型,你要么先转换为一个objec类型再转换为值类型,要么就直接转换为一个空值类型,如int。
下面是关于EnumerationExtensions类的所有代码。如果你有什么好的建议,请告知我。我目前正在进行修订来完善这段代码。
namespace
Enum.Extensions {
public static class EnumerationExtensions {
// checks if the value contains the provided type
public static bool Has < T > ( this System.Enum type, T value) {
try {
return ((( int )( object )type & ( int )( object )value) == ( int )( object )value);
}
catch {
return false ;
}
}
// checks if the value is only the provided type
public static bool Is < T > ( this System.Enum type, T value) {
try {
return ( int )( object )type == ( int )( object )value;
}
catch {
return false ;
}
}
// appends a value
public static T Add < T > ( this System.Enum type, T value) {
try {
return (T)( object )((( int )( object )type | ( int )( object )value));
}
catch (Exception ex) {
throw new ArgumentException(
string .Format(
" Could not append value from enumerated type '{0}'. " ,
typeof (T).Name
), ex);
}
}
// completely removes the value
public static T Remove < T > ( this System.Enum type, T value) {
try {
return (T)( object )((( int )( object )type & ~ ( int )( object )value));
}
catch (Exception ex) {
throw new ArgumentException(
string .Format(
" Could not remove value from enumerated type '{0}'. " ,
typeof (T).Name
), ex);
}
}
}
}
public static class EnumerationExtensions {
// checks if the value contains the provided type
public static bool Has < T > ( this System.Enum type, T value) {
try {
return ((( int )( object )type & ( int )( object )value) == ( int )( object )value);
}
catch {
return false ;
}
}
// checks if the value is only the provided type
public static bool Is < T > ( this System.Enum type, T value) {
try {
return ( int )( object )type == ( int )( object )value;
}
catch {
return false ;
}
}
// appends a value
public static T Add < T > ( this System.Enum type, T value) {
try {
return (T)( object )((( int )( object )type | ( int )( object )value));
}
catch (Exception ex) {
throw new ArgumentException(
string .Format(
" Could not append value from enumerated type '{0}'. " ,
typeof (T).Name
), ex);
}
}
// completely removes the value
public static T Remove < T > ( this System.Enum type, T value) {
try {
return (T)( object )((( int )( object )type & ~ ( int )( object )value));
}
catch (Exception ex) {
throw new ArgumentException(
string .Format(
" Could not remove value from enumerated type '{0}'. " ,
typeof (T).Name
), ex);
}
}
}
}