枚举、标记和C#

原文地址:http://www.codeproject.com/Articles/37921/Enums-Flags-and-Csharp-Oh-my-bad-pun.aspx
我不知道其他人怎么样,但是我就是喜欢枚举类型。而且,我也喜欢和枚举结合在一起的Flags Attribute(不知道翻成什么好)。本文将探讨配合着扩展方法如何使用二者来使你的代码更紧凑、易懂。
如果你从来没有将二者结合起来过,请不要错过。考虑下面的代码:
class User {
    
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;
}
上面这个枚举的优点就在于我们可以将多个值分配到相同的属性。不仅如此,我们还可以通过比较,对已存值进行检测。
//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
现在代码看起来非常简短易读。但是,当你要检测一个值的时候,每次都需要把代码重写一遍。那还不是很糟,但是我真的不想经常的去敲代码。当然,你可以写一个单独的函数来做这个比较,但是相对而言,我们还更很好的方法。
发挥扩展方法的优势
因为枚举类型并不是一个类,所以,并不能对枚举类型进行方法扩展。然而,在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;
        }
    }
}
现在,这段代码假设枚举类型可以转化为整型。在做比较之前可以做一些类型检测,但是,这个例子的初衷是要保持代码简短。
那么,如何来使用这个扩展呢?
//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
现在,代码变得更加易读了。甚至,可以注意到,这个扩展有一个通用参数,在使用这个方法之前我们不必提供参数类型,因为方法本身能够根据参数推断出来。
同时,也要记住,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);
            }
        }
    }
}


posted on 2009-09-13 12:01  火柴没帽  阅读(374)  评论(0编辑  收藏  举报

导航