在处理表时如何编写清晰且可维护的代码?

时间:2015-10-09 13:58:33

标签: excel vba excel-vba worksheet listobject

在我的项目中,我经常利用表和底层的ListObjects和ListColumns。我喜欢它们,因为它们比裸露的Range对象更容易引用和更新。然而,我仍然没有找到一种理智且可维护的方法来处理由多个ListColumns组成的多个ListObject,并在项目的所有工作表中引用。

让我们说我的工作表((名称)属性设置为" WorksheetA")包含几个列的表(称为TableA)(称为Column1,Column2,...,Column10) )。

现在我想引用另一个工作表代码中的一个列。我可以这样做:

WorksheetA.ListObjects("TableA").ListColumns("Column7")

现在,直接使用字符串是一种不好的做法,因为它很难维护并且容易出错。

那么现在呢?

我可以创建专用模块来存储我的字符串作为常量。例如,模块名为"常量":

Public Const TABLE_A As String = "TableA"
Public Const COLUMN7 As String = "Column7"

然后我的引用可以转换为:

WorksheetA.ListObjects(Constants.TABLE_A).ListColumns(Constants.COLUMN7)

但是,这种解决方案有一些缺点:

  1. 添加每个表和列时,常量模块会快速增长。
  2. 参考本身变得越来越难以理解。
  3. 与所有工作簿中的表相关的所有常量都被抛入一个巨大的坑中。
  4. 我可以在WorksheetA中存储常量,并通过公共函数使它们可用,如:

    Private Const TABLE_A As String = "TableA"
    Private Const COLUMN7 As String = "Column7"
    
    Public Function GetTableAName() As String
        GetTableAName = TABLE_A
    End Function
    
    Public Function GetTableA() As ListObject
        Set GetTableA = WorksheetA.ListObjects(TABLE_A)
    End Function
    
    Public Function GetTableAColumn7() As ListColumn
        Set GetTableAColumn7 = GetTableA().ListColumns(COLUMN7)
    End Function
    

    这个解决方案实际上解决了上面提到的所有三个问题,但它仍然有点"脏"并且耗时,因为添加新表引入了为每列创建函数的要求。

    你更了解如何处理这个问题吗?

    EDIT1(为清晰起见):我们假设用户不得更改任何名称(表名和列名)。如果用户这样做,那就是他/她的责任。

    EDIT2(为清晰起见):我仅将Column7用作列名作为示例。让我们假设列有更有意义的名称。

1 个答案:

答案 0 :(得分:1)

这是我的两分钱。我不是一个受过良好教育的程序员,但我确实得到报酬,所以我想这让我很专业。

第一道防线是我创建了一个用于建模表的类。我从表中填写类,没有其他代码甚至知道数据的位置。初始化时,我会运行像

这样的代码
clsEmployees.FillFromListObject wshEmployees.ListObjects(1)

然后在课堂上,代码看起来像

vaData = lo.DataBodyRange.Value
...
clsEmployee.EeName = vaData(i,1)
clsEmployee.Ssn = vaData(i,2) 
etc

每个工作表只有一个ListObject。这是我的规则,我永远不会破坏它。有权访问工作表的任何人都可以重新排列列并破坏我的代码。如果我想使用Excel作为数据库,有时我会这样做,那么这就是我冒的风险。如果它非常重要以至于我无法承担这种风险,那么我将数据存储在SQL Server,SQLite或JET中。

我可以实际调用ListColumns名称,而不是将范围放在数组中。这样,如果有人重新安排了列,我的代码仍然有效。但它引入了他们可以重命名列,所以我只是将一个风险换成另一个。它会使代码更具可读性,因此它可能是您想要进行的交易。我喜欢从阵列中填充的速度,这就是我所做的交易。

如果我的项目足够小或者应该直接使用ListObjects,那么我遵循与任何字符串相同的规则。

  • 我在代码中只使用了一次字符串。
  • 如果我多次使用它,我会制作一个程序级常量
  • 如果我在多个程序中使用它,我会尝试将其作为参数传递
  • 如果我不能将其作为参数传递,我会创建一个模块级常量
  • 如果这两个程序在不同的模块中,我首先问自己为什么两个程序在不同的模块中使用相同的常量。相关的程序不应该在同一个模块中吗?
  • 如果这两个程序确实属于不同的模块,那么我尝试将其作为参数传递
  • 如果这些都不起作用,那么它确实是一个全局常量,我在我的MGlobals模块中设置。

如果MGlobals占用超过半个屏幕,我做错了什么,我需要退后一步思考我想要完成的事情。然后我做了一个自定义课程。