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

Vba 如何在处理表时编写清晰且可维护的代码?,vba,excel,worksheet,listobject,Vba,Excel,Worksheet,Listobject,在我的项目中,我经常利用表、底层的ListObjects和ListColumns。我喜欢它们,因为它们比裸范围对象更容易引用和更新。然而,我仍然没有找到一种合理且可维护的方法来处理由许多ListColumn组成的多个ListObject,并且在项目中的所有工作表中都被引用 假设我有一个工作表(将(Name)属性设置为“WorksheetA”),其中包含一个表(称为TableA),该表只有很少的列(称为Column1、Column2、…、Column10) 现在我想引用另一个工作表代码中的一列。我

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

假设我有一个工作表(将(Name)属性设置为“WorksheetA”),其中包含一个表(称为TableA),该表只有很少的列(称为Column1、Column2、…、Column10)

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

WorksheetA.ListObjects("TableA").ListColumns("Column7")
现在,直接使用字符串是一种不好的做法,因为它很难维护并且容易出错

那现在呢

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

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

WorksheetA.ListObjects(Constants.TABLE_A).ListColumns(Constants.COLUMN7)
但是,此解决方案有一些缺点:

  • 常量模块会随着每个表和列的添加而以惊人的速度增长
  • 引用本身会增长并变得不那么可读
  • 所有工作簿中与表格相关的所有常量都被扔进一个巨大的坑中
  • 我可以将常量存储在工作表中,并通过以下公共功能使其可用:

    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作为列名作为示例。让我们假设列的名称更有意义。

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

    第一道防线是我创建一个类来建模一个表。我从表中填充类,没有其他代码知道数据的位置。当我初始化时,我将运行如下代码

    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占据了超过半个屏幕,我就做错了,我需要后退一步,仔细想想我要完成什么。然后我创建一个自定义类。

    有趣的问题。如果有一个简单的解决方案,我想我们会看到它应用于数据访问库,如DAO、ADO、JDBC等。开发人员要封装这些“字段名”和“表名”,唯一能做的就是编写“持久”类,即每个表或数据对象一个类。这是“干净”的解决方案,但它增加了一层,而且很耗时。建议用于大型数据库项目,但我怀疑Excel项目是否会达到如此规模,以致于此解决方案变得非常必要。我猜您担心在字符串方法中,用户可能会更改表名,从而破坏引用。如果工作表上只有一个表,则可以使用索引号(1)而不是字符串。这仅对每张图纸最多一张表有效。不知道,如果这适用于你所有的床单。如果你的列名真的像
    Column7
    那样难以描述,那么为什么不通过数字索引来引用它们呢?我是常量的大力支持者,但我认为你的想法太过分了。我一点也不在乎这个。你做了很多工作却毫无益处。它不会阻止用户更改表或列的名称。然后就要看你想对这些列做什么了。我主要在VBA中使用数组。因此,您不必处理所有引号和对象引用,而是可以从数组中的结构化引用中获取列的所有值,如下所示:
    v=[Table1[Column7]]
    您还可以创建一个通用的ListColumn返回函数。将ws-name、表名和列名传递给它,它将返回ListColumn对象。我不会在任何地方定义这些东西的名称。
    vaData = lo.DataBodyRange.Value
    ...
    clsEmployee.EeName = vaData(i,1)
    clsEmployee.Ssn = vaData(i,2) 
    etc