【问题标题】:Passing worksheet as argument to a sub routine将工作表作为参数传递给子例程
【发布时间】:2015-11-24 16:20:15
【问题描述】:

我知道关于这个主题有很多问题,但我仍在为超出范围的下标而苦苦挣扎?是的,我正在导入的工作簿确实有一张名为LSL Recon 的工作表,我已验证。我已调试并将LSL Recon 替换为Import Sheets(1)(如Sheet1),然后该过程会继续进行一点,但不会将任何内容导入数组。

Option Explicit

Public FILENAME, c_DATE, cNAME As String
'Public ws As Worksheet

Sub main()
    Application.DisplayAlerts = True
    Application.ScreenUpdating = False

    CLEAR
    Import Sheets("LSL Recon")
    Display_Import
End Sub()

Sub Import(ws As Worksheet)  
    Workbooks.Open FILENAME
    Set TempBook = ActiveWorkbook
    ws.Activate

    cNAME = "Entity"
    cA = Sheets(1).Rows.Find(What:=UCase(cNAME), LookAt:=xlWhole, SearchDirection:=xlNext).Column
    cNAME = "Sector"
    cB = Sheets(1).Rows.Find(What:=UCase(cNAME), LookAt:=xlWhole, SearchDirection:=xlNext).Column
    cNAME = "Date"
    cC = Sheets(1).Rows.Find(What:=UCase(cNAME), LookAt:=xlWhole, SearchDirection:=xlNext).Column
    cNAME = "Client"
    cD = Sheets(1).Rows.Find(What:=UCase(cNAME), LookAt:=xlWhole, SearchDirection:=xlNext).Column
    ...
End Sub()

有用的问题:
VBA: Calling a sub on another worksheet with multiple arguments
pass sheet to a function (excel vba)
Passing a Worksheet to a subroutine

【问题讨论】:

  • 您试图在活动工作簿中引用名为“LSL Recon”的工作表,而不是在您将要打开的工作簿中。如果您尝试引用即将打开的工作簿中的工作表,则必须将工作表 name 作为字符串参数传递。
  • 进一步了解@Rory 所说的,如果您将名称作为字符串传递,您仍然需要在 sub 中设置工作表 ... 就像 Set ws = TempBook.Sheets(sName) 假设 sName 是您传递给子的字符串,而不是 ws As Worksheet

标签: vba excel


【解决方案1】:

摆脱你的公共范围变量,声明所有变量,并根据需要传递参数:

Option Explicit

Sub main()

    Dim FILENAME$
    Dim c_DATE$
    Dim cNAME$
    Dim wsName$
    wsName = "LSL Recon"
    Application.DisplayAlerts = True
    Application.ScreenUpdating = False

    CLEAR
    Import (wsName)
    Display_Import
End Sub

Sub Import(wsName$)  
    Dim wb as Workbook
    Dim cNames, itm, found
    ' Use an array of items to search for
    cNames = Split("Entity,Sector,Date,Client",",")

    Set wb = Workbooks.Open(FILENAME)

    Set ws = wb.Sheets(wsName)

    For Each itm in cNames
        found = ws.Rows.Find(What:=UCase(cNAME), LookAt:=xlWhole, SearchDirection:=xlNext).Column
        Debug.Print cName " found in column: " & found
    Next

End Sub

如果需要将.Find的结果返回给主过程,则将其改为Function,返回一个集合对象,这样调用:

Set foundItems = Import(wsName)
Dim itm
For each itm in foundItems
    Debug.Print itm
Next

然后是函数:

Function Import(wsName$)  
    Dim wb as Workbook
    Dim ret as New Collection
    Dim cNames, itm, found
    ' Use an array of items to search for
    cNames = Split("Entity,Sector,Date,Client",",")

    Set wb = Workbooks.Open(FILENAME)

    Set ws = wb.Sheets(wsName)

    For Each itm in cNames
        ret.Add ws.Rows.Find(What:=UCase(cNAME), LookAt:=xlWhole, SearchDirection:=xlNext).Column
        Debug.Print cName " found in column: " & ret(ret.Count)
    Next
    'return the collection to the calling procedure
    Set Import = ret
End Function

【讨论】:

  • 当你声明 Sub Import(wsName) 为什么你要传递 Import Sheets(wsName)?首先,您应该始终定义参数和变量的类型(Dim cNames,itm,例如找到声明非类型安全的 Variant 变量)。如果您按照应有的方式将参数定义为字符串,则在尝试传递整个工作表而不是工作表名称时会出现运行时错误!
  • @cboden 我将函数定义更改为需要字符串,工作表对象的传递是一个错误,我在 OP 的代码中没有注意到(公平地说,有 很多 i> 源中的错误),我也修改了它。其他变量是类型变量,不需要明确的As Variant 声明,Dim cNames, itm, found 就足够了。
  • 不声明变量的类型仍然是一种不好的风格(而且函数的返回值类型仍然缺失)。这些 Variant var 在编译器中造成大量开销,因为每次访问此类 var 时都必须进行隐式类型转换。所以很浪费资源。此外,类型 save vars 有助于输入正确的值,正如您在期望字符串的工作表示例中看到的那样。如果你不编写类型安全的程序,这样的错误很难找到。
  • @cboden 如果您认为可以改进,欢迎您对我的回答进行修改
猜你喜欢
  • 2013-09-11
  • 1970-01-01
  • 2015-06-18
  • 1970-01-01
  • 2023-03-05
  • 1970-01-01
  • 1970-01-01
  • 2012-10-09
相关资源
最近更新 更多