zoukankan      html  css  js  c++  java
  • 小心枚举陷阱

          枚举类型无疑给我们开发带来了很大的便利,智能感应+代码可读性就让我们有足够的理由在项目中使用。但枚举类型使用不当也存在着温柔陷阱,而且陷阱很难发现。

          事情的缘起是这样的,项目A是公共基类,很多项目都会引用,项目B会引用项目A,但由于项目B使用人数不多而且功能很稳定,所以部署的时候一般不会传项目B的dll文件。但前前几天项目B突然出错了,报错的“找不到业务类型对应的数据库连接”。本地调试项目B,一点问题没有,可线上的就是出错,重新上传了项目B的dll,神奇般的好了。感觉有些莫名奇妙,一点都没改动,重新上传下dll就好了,是什么原因呢?没有无缘无故的爱,同样也没有无缘无故的错误,《温柔的枚举陷阱》一文帮我找到了答案,问题的根源是项目A中枚举类型的更改。

          我们可以从下面的示例中找到答案。ProjectA是很多项目共用的基础类库,ProjectB会添加ProjectA的引用,但二者并不在一个解决方案下面,更新ProjectA时会把A的dll文件放到B的bin目录下面。

    项目ProjectA的代码示例
    namespace ProjectA
    {
    /// <summary>
    /// 业务类型
    /// </summary>
    public enum BusinessType
    {
    UserRead,
    UserWrite,
    ProductRead,
    ProductWrite
    }

    public class DBHelper
    {
    /// <summary>
    /// 根据业务类型获取连接字符串
    /// </summary>
    public static string GetConnectString(BusinessType bt)
    {
    string connectString = string.Empty;
    switch (bt)
    {
    case BusinessType.UserRead:
    connectString
    = "UserRead";
    break;
    case BusinessType.UserWrite:
    connectString
    = "UserWrite";
    break;
    case BusinessType.ProductRead:
    connectString
    = "ProductRead";
    break;
    case BusinessType.ProductWrite:
    connectString
    = "ProductWrite";
    break;
    default:
    connectString
    = "UserRead";
    break;
    }
    return connectString;
    }
    }
    }

          项目B添加对项目A的引用后,其代码如下。

    项目ProjectB代码示例
    using ProjectA;

    namespace ProjrctB
    {
    public class B
    {
    public static void GetUser(BusinessType bt)
    {
    //根据BusinessType选择合适的数据库获取数据
    string connectString = DBHelper.GetConnectString(bt);
    Console.WriteLine(connectString);
    }
    }
    }

           执行ProjrctB.B.GetUser(BusinessType.ProductRead)后毫无疑问输出“ProductRead”,和我们预期的一致。接下来业务类型扩展了,要在BusinessType中增加评论类型,修改后的代码如下。

    项目ProjectA业务类型扩展后的代码示例
    namespace ProjectA
    {
    /// <summary>
    /// 业务类型
    /// </summary>
    public enum BusinessType
    {
    CommentRead,
    CommentWrite,
    UserRead,
    UserWrite,
    ProductRead,
    ProductWrite
    }

    public class DBHelper
    {
    /// <summary>
    /// 根据业务类型获取连接字符串
    /// </summary>
    public static string GetConnectString(BusinessType bt)
    {
    string connectString = string.Empty;
    switch (bt)
    {
    case BusinessType.CommentRead:
    connectString
    = "CommentRead";
    break;
    case BusinessType.CommentWrite:
    connectString
    = "CommentWrite";
    break;
    case BusinessType.UserRead:
    connectString
    = "UserRead";
    break;
    case BusinessType.UserWrite:
    connectString
    = "UserWrite";
    break;
    case BusinessType.ProductRead:
    connectString
    = "ProductRead";
    break;
    case BusinessType.ProductWrite:
    connectString
    = "ProductWrite";
    break;
    default:
    connectString
    = "UserRead";
    break;
    }
    return connectString;
    }
    }
    }

           重新生成ProjectA,将其dll部署到ProjectB的bin目录下面,这时候执行ProjrctB.B.GetUser(BusinessType.ProductRead)还会得到我们期待的结果吗?答案是否定的,执行后得到的结果是“UserRead”,并不是我们想要的“ProductRead”类型。为什么会出现这种情况呢?

           枚举值本质上就是个整数值,代码编译后,dll中只保留该枚举的整数值,而不会保留枚举变量名。如果枚举本身发生变化,如示例中在枚举BusinessType前面插入了两项,那么后面枚举变量的值都会改变的(如果不是显示指定值的话)。这个时候如果只重新编译了枚举本身,而没有编译引用枚举的地方ProjectB,那么就会出现张冠李戴的问题,因为整数值代表的枚举类型已经变了。这个问题解决的办法是创建枚举时显示指定枚举变量的值,扩展修改枚举类型时不要改变枚举变量的值。如下面的代码,添加业务类型时,将CommentRead = 4和CommentWrite = 5加入即可,而不要动以前的业务类型UserRead = 0,这样扩展类型时及时不去重新编译ProjectB,也不会出现上述问题了。

    显示指定枚举变量的值
    /// <summary>
    /// 业务类型
    /// </summary>
    public enum BusinessType
    {
    CommentRead
    = 4,
    CommentWrite
    = 5,
    UserRead
    = 0,
    UserWrite
    = 1,
    ProductRead
    = 2,
    ProductWrite
    = 3
    }

            问题的本质在于编译时直接用变量的值代替了变量的名称,不仅枚举类型存在这个问题,常量同样也存在该问题,如果更新了常量的值,而没用编译引用到常量的项目,那么常量值的修改对引用到它的项目来说是无效的,因此有人建议除了值固定不变的常量(如圆周率),可以用readonly代替常量。

            如果不是看到类似问题的文章,这种问题很难找到答案,写出来对自己是一种提高,也希望能给遇到类似问题的朋友一些帮助。

  • 相关阅读:
    这几个C++的坑,一旦踩中了,加班是肯定避免不了了!
    2020年11月编程语言排行榜:Java再掉一位排名,真的要跌落神坛了吗?
    自学编程的朋友想要了解怎么快速入门,我想给你这5个建议!
    来看看这些获奖的C语言程序!一个比一个秀,这就是强者的世界吗!
    【每天学点新知识】Linux操作系统下C语言多线程同步使用指南!
    听说IT行业只有高智商的人才能进得去,而且以男性居多,还必须专业对口?
    假如C++是一只箭,你会用它来射哪只雕?
    程序员常见的口头禅,最后一个笑掉大牙,网友:真实的一批!
    Python基础语法
    Python—“helloworld”
  • 原文地址:https://www.cnblogs.com/freshman0216/p/1780023.html
Copyright © 2011-2022 走看看