VBA:如何通过集合、“迟到”字典、其他方式关联多个对象实例?

Posted

技术标签:

【中文标题】VBA:如何通过集合、“迟到”字典、其他方式关联多个对象实例?【英文标题】:VBA: How to associate multiple object instances via collections, 'late' dictionary, other ways? 【发布时间】:2021-12-30 05:54:48 【问题描述】:

我是 VBA 的新手,但我已经学到了很多东西并且从未发布过任何东西,所以如果我可能不清楚或遵循任何预期的约定,我深表歉意。 我创建了一个当前有 1 个类的接口。 我有一个子获取多个工作表,相关的命名范围和行数量,我用它来循环我需要组合在一起的分散数据。 一旦逐行读取数据,它就会实例化对象(该类通过从模块接收范围的公共 init 方法将数据从单元格中提取出来),然后我将它们添加到模块中的一个没有键的简单集合中。直到一切正常为止。

在某些情况下,我需要将对象实例相互关联,因为我需要在稍后阶段以稍微不同的方式处理它们。 (之后输出表中的更多列) 我在我的数据中添加了一个唯一标识符,仅由相关实例共享,但我很困惑。 我不知道如何从这里着手创建这样的协会。 这是阅读:

'Create solution is placed between Class creation and sub to define the target sheets & ranges
Option Explicit
Sub ReadData(Solutions As Collection)

Set Solutions = New Collection

Dim Solution As Variant
Dim ws As Worksheet
Dim rng As Range
Dim rowamount As Long

'define length of range
rowamount = Worksheets("source").Range("Named_ranges").Rows.Count

Dim myrow As Integer
Dim suspectWorksheet As String
Dim TargetWorksheet As Worksheet
Dim TargetWorkRange As String
Dim TargetRangeCount As Integer
Dim x As Integer

For myrow = 1 To rowamount

    'Identify the visible sheets from the source matrix & init worksheet
    suspectWorksheet = Worksheets("source").Range("Named_ranges").Cells(myrow, 1)
    Set TargetWorksheet = Worksheets(suspectWorksheet)
    If TargetWorksheet.Visible = True Then
    
    ' Init the range variable and get the max amount of lines to scan
    TargetWorkRange = Worksheets("source").Range("Named_ranges").Cells(myrow, 2)
    TargetRangeCount = Worksheets("source").Range("Named_ranges").Cells(myrow, 3)

    ' Start the lineloop to inject the data into the class
       For x = 1 To TargetRangeCount
       Debug.Print "Loop " & x
            'Is there an active line in the target range?
            If Worksheets(suspectWorksheet).Range(TargetWorkRange).Cells(x + 1, 1) > 0 Then
                 Set rng = Worksheets(suspectWorksheet).Range(TargetWorkRange).Resize(1, 60).Offset(x, 0)
                 Set Solution = solutionClassFactory(rng)
                 Solutions.Add Solution
                 
                 'Solution.PrintOut
             End If
       Next x

     End If
    

Next myrow



 Set TargetWorksheet = Nothing
End Sub

' Checks the type of solution and returns into a class
Function solutionClassFactory(rng As Range) As Variant

Dim solutionType As String

solutionType = rng.Cells(1, 51)

Dim Solution As Variant
Select Case solutionType
    Case "something":
        Set Solution = New something
End Select

Solution.Init rng

' solution is returned to be added to the main collection
Set solutionClassFactory = Solution

End Function

这是写作部分:

Sub Create()
Dim Solution As Variant
Dim Solutions As Collection
Dim TargetWorksheet As String
Dim i As Integer
'Define to which sheet it needs to be written
TargetWorksheet = "sheet"

ReadData Solutions
i = 5

For Each Solution In Solutions

Worksheets(TargetWorksheet).Cells(i, 1) = Solution.amount
'more

i = i +1


Next Solution


End Sub

非常感谢您的帮助,我已经从 *** 社区学到了很多东西,但是在这里我真的很困惑,出于性能原因,我不想恢复到循环中的循环。

编辑:添加类代码

' class derived from Solution interface
Option Explicit

 ' Implements Solution interfacs
Implements Solution

Private amount_ As Integer
Private amountRef_ As String


Private Sub Class_Initialize()


End Sub

Public Sub Init(rng As Range)

    amount_ = rng.Cells(1, 1)
    amountRef_ = "'" & rng.Parent.Name & "'!" & rng.Columns.Item(1).address
    
End Sub

Public Sub PrintOut()
Debug.Print amount_, TypeName(Me), linekey_ & vbNewLine;
Debug.Print amountRef_, TypeName(Me), linekeyRef_ & vbNewLine;
End Sub

Private Sub Class_Terminate()
    ' Debug.Print "WAN class instance deleted"
End Sub

Public Property Get amount() As Integer
    amount = amount_
End Property

Public Property Let amount(ByVal Value As Integer)
    amount = amount_
End Property

Public Property Get linekeyRef() As String
    linekeyRef = linekeyRef_
End Property

Public Property Let linekeyRef(ByVal Value As String)
    linekeyRef = linekeyRef_
End Property

' Implement required interface properties
Private Property Get Solution_address() As String
    Solution_address = address
End Property

【问题讨论】:

你是如何创建“唯一标识符”的? 只是与导入到 excelsheet 的范围内的单元格相关的类的属性,就像任何其他属性一样。 也许我应该重新表述我想要实现的目标。例如,我希望有多个附加集合,其中只有这些对象实例,这些对象实例由该属性组合在一起。所以重要的属性将是 2 个实例,不重要的属性将是 3 个实例,所有其他实例仍应在原始解决方案集合中。希望这是有道理的 :) 我想我明白你想要什么,字典是一种解决方案,但因为你只显示了部分代码,我不确定你是否充分利用了类对象的全部好处。当您同时使用 OOP 和结构化编程时,危险就在于过于复杂。 老实说,我认为你是对的。基本上除了获取范围和单元格值的 init 方法之外,我在类本​​身中并没有做很多事情。我基本上遮蔽了值和单元格引用(以创建动态和静态工作表)。然而,我正在尝试创造一些强大而灵活的东西。但收到消息,因此将添加减少变量的代码。 【参考方案1】:

使用以您的唯一 id 作为键并以对象集合作为值的 Dictionary 对象。例如,一些用于创建对象和调用方法的***代码。

Option Explicit

Sub Process()

   Dim rep As reporter
   Set rep = New reporter
   Set rep.SourceRng = Sheets("source").Range("Named_ranges")
   rep.readata
   MsgBox rep.linecount & " lines read"
   
   Set rep.DestRng = Sheets("Sheet5").Range("A1")
   rep.writedata
  
   Set rep.DestRng = Sheets("Sheet6").Range("A1")
   rep.writedata_bytyp
   MsgBox "Done"
   
End Sub

Solution

Public amount As Long
Public ref As String
Public typ As String

一个Reporter 类来保存字典和集合

Private Solutions As New Collection
Private Things As Object
Public SourceRng As Range
Public DestRng As Range
Public linecount As Long
Const COL_TYPE = "AY" '51

Sub readata()

   Dim i As Long, obj As Solution, v
   Dim ws As Worksheet, sRng As String, rng As Range
   Dim r As Long, rowcount As Long
   Set Things = CreateObject("Scripting.Dictionary")
   
   For i = 1 To SourceRng.Rows.Count
        Set ws = Sheets(SourceRng.Cells(i, 1).Value2)
        sRng = SourceRng.Cells(i, 2)
        rowcount = SourceRng.Cells(i, 3)
        
        If ws.Visible = True Then
            Set rng = ws.Range(sRng)
            For r = 2 To rowcount + 1
            
                v = rng.Cells(r, 1).Value2
                If v > 0 Then
                      
                    Set obj = New Solution
                    obj.amount = v
                    obj.typ = Trim(rng.Cells(r, COL_TYPE))
                    obj.ref = ws.Name & "!" & rng.Cells(r, 1).Address
                    Solutions.Add obj
                    
                    If Not Things.exists(obj.typ) Then
                        Things.Add obj.typ, New Collection
                    End If
                    Things(obj.typ).Add obj
                    linecount = linecount + 1
                End If
            Next
        End If
    Next
End Sub

Sub writedata()
    Dim i As Long, obj
    With DestRng
        For Each obj In Solutions
           i = i + 1
           .Cells(i, 1) = obj.amount
           .Cells(i, 2) = obj.typ
           .Cells(i, 3) = obj.ref
        Next
    End With
End Sub

Sub writedata_bytyp()
    Dim i As Long, key, obj
    With DestRng
        For Each key In Things.keys
            i = i + 1
            .Cells(i, 1) = key
            For Each obj In Things(key)
                i = i + 1
                .Cells(i, 2) = obj.amount
                .Cells(i, 3) = obj.typ
                .Cells(i, 4) = obj.ref
            Next
        Next
    End With
End Sub

【讨论】:

当我阅读代码时,它似乎确实按照我的意愿行事。我会测试并让你知道。非常感谢您的帮助。 我没有足够的声望来投票,但这是出色的编码。这就像一个魅力,它被简化、强大但在未来易于管理。所以我投了大票:)

以上是关于VBA:如何通过集合、“迟到”字典、其他方式关联多个对象实例?的主要内容,如果未能解决你的问题,请参考以下文章

在 VBA 中的字典值中查找最大值/最小值

填充字典导致错误

如何在Excel VBA中使用字典Dictionary对象

关联数组与多维数组,VBA

推导式(列表, 集合, 字典), 生成器

推导式(列表, 集合, 字典), 生成器