美文网首页
[转]C#枚举高级战术

[转]C#枚举高级战术

作者: wwmin_ | 来源:发表于2020-05-17 14:00 被阅读0次

    转自:Liam Wang
    cnblogs.com/willick/p/csharp-enum-superior-tactics.html

    文章开头先给大家出一道面试题:

    在设计某小型项目的数据库(假设用的是 MySQL)时,如果给用户表(User)添加一个字段(Roles)用来存储用户的角色,你会给这个字段设置什么类型?提示:要考虑到角色在后端开发时需要用枚举表示,且一个用户可能会拥有多个角色

    image.png

    枚举基础

    枚举类型的作用是限制其变量只能从有限的选项中取值,这些选项(枚举类型的成员)各自对应于一个数字,数字默认从 0 开始,并以此递增。例如:

    public enum Days
    {
        Sunday, Monday, Tuesday, // ...
    }
    

    其中 Sunday 的值是 0,Monday 是 1,以此类推。为了一眼能看出每个成员代表的值,一般推荐显示地将成员值写出来,不要省略:

    public enum Days
    {
        Sunday = 0, Monday = 1, Tuesday = 2, // ...
    }
    
    image.png
    // 枚举转字符串
    string foo = Days.Saturday.ToString(); // "Saturday"
    string foo = Enum.GetName(typeof(Days), 6); // "Saturday"
    
    // 字符串转枚举
    Enum.TryParse("Tuesday", out Days bar); // true, bar = Days.Tuesday
    (Days)Enum.Parse(typeof(Days), "Tuesday"); // Days.Tuesday
    
    // 枚举转数字
    byte foo = (byte)Days.Monday; // 1
    
    // 数字转枚举
    Days foo = (Days)2; // Days.Tuesday
    
    // 获取枚举所属的数字类型
    Type foo = Enum.GetUnderlyingType(typeof(Days))); // System.Byte
    
    // 获取所有的枚举成员
    Array foo = Enum.GetValues(typeof(MyEnum);
    
    // 获取所有枚举成员的字段名
    string[] foo = Enum.GetNames(typeof(Days));
    
    image.png
    public enum ApiStatus
    {
        [Description("成功")]
        OK = 0,
        [Description("资源未找到")]
        NotFound = 2,
        [Description("拒绝访问")]
        AccessDenied = 3
    }
    
    static class EnumExtensions
    {
        public static string GetDescription(this Enum val)
        {
            var field = val.GetType().GetField(val.ToString());
            var customAttribute = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute));
            if (customAttribute == null) { return val.ToString(); }
            else { return ((DescriptionAttribute)customAttribute).Description; }
        }
    }
    
    static void Main(string[] args)
    {
        Console.WriteLine(ApiStatus.Ok.GetDescription()); // "成功"
    }
    

    上面这些我认为已经包含了大部分我们日常用到的枚举知识了。下面我们继续回到文章开头说的用户角色存储问题。

    用户角色存储问题

    我们先定义一个枚举类型来表示两种用户角色:

    public enum Roles
    {
        Admin = 1,
        Member = 2
    }
    
    image.png
    public class User
    {
        public int Id { get; set; }
        public Roles Roles { get; set; }
    }
    
    connection.Query<User>(
        "SELECT * FROM `User` WHERE `Roles` & @roles = @roles;",
        new { roles = Roles.Admin | Roles.Member });
    

    对应的,在 C# 中要判断用户是否拥有某个角色,可以这么判断:

    // 方式一
    if ((user.Roles & Roles.Admin) == Roles.Admin)
    {
        // 做管理员可以做的事情
    }
    
    // 方式二
    if (user.Roles.HasFlag(Roles.Admin))
    {
        // 做管理员可以做的事情
    }
    

    同理,在 C# 中你可以对枚举进行任意位逻辑运算,比如要把角色从某个枚举变量中移除:

    var foo = Roles.Admin | Roles.Member;
    var bar = foo & ~Roles.Admin;
    

    这就解决了文章前面提到的用整型来存储多角色的问题,不论数据库还是 C# 语言,操作上都是可行的,而且也很方便灵活。

    枚举的 Flags 特性

    image.png
    [Flags]
    public enum Roles
    {
        Admin = 1,
        Member = 2
    }
    

    加上这个 Flags 特性后,我们再来调试 GetUsersInRoles(Roles roles) 方法时,roles 参数的值就会显示为 Admin|Member 了。简单来说,加不加 Flags 的区别是:

    var roles = Roles.Admin | Roles.Member;
    Console.WriteLing(roles.ToString()); // "3",没有 Flags 特性
    Console.WriteLing(roles.ToString()); // "Admin, Member",有 Flags 特性
    

    给枚举加上 Flags 特性,我觉得应当视为 C# 编程的一种最佳实践,在定义枚举时尽量加上 Flags 特性。
    解决枚举值冲突:2 的幂
    到这,枚举类型 Roles 一切看上去没什么问题,但如果现在要增加一个角色:Mananger,会发生什么情况?按照数字值递增的规则,Manager 的值应当设为 3。

    [Flags]
    public enum Roles
    {
        Admin = 1,
        Member = 2,
        Manager = 3
    }
    
    image.png
    1:  00000001
    2:  00000010
    4:  00000100
    8:  00001000
    

    再往后增加的话就是 16、32、64...,其中各值不论怎么相加都不会和成员的任一值冲突。这样问题就解决了,所以我们要这样定义 Roles 枚举的值:

    [Flags]
    public enum Roles
    {
        Admin = 1,
        Member = 2,
        Manager = 4,
        Operator = 8
    }
    

    不过在定义值的时候要在心中小小计算一下,如果你想懒一点,可以用下面这种“位移”的方法来定义:

    [Flags]
    public enum Roles
    {
        Admin    = 1 << 0,
        Member   = 1 << 1,
        Manager  = 1 << 2,
        Operator = 1 << 3
    }
    
    image.png

    相关文章

      网友评论

          本文标题:[转]C#枚举高级战术

          本文链接:https://www.haomeiwen.com/subject/uuzjohtx.html