VBA связывает два объекта в двух направлениях

У меня есть два объекта, clsGroup и clsUser. Пользователи и группы являются отдельными объектами.

  • Каждый пользователь может состоять в любом количестве групп.
  • Каждая группа может содержать любое количество пользователей.

Вместо того, чтобы хранить список групп, принадлежащих каждому пользователю, и список пользователей, принадлежащих к каждой группе в каждом объекте, предлагает ли VBA какой-либо способ связать их, чтобы из пользовательского объекта мы могли получить список групп, связанных с этого пользователя, и из объекта группы мы можем получить список пользователей, связанных с этой группой?

Я стараюсь избегать создания коллекций внутри пользователей и групп, так как синхронизация будет проблематичной.


person lfrandom    schedule 23.11.2015    source источник


Ответы (1)


Это общая проблема циклических ссылок. Там много всего об этом, и общий вывод состоит в том, что их следует избегать как можно больше.

Поэтому вы правы, когда отказываетесь хранить в каждом классе набор ссылок на другой класс. В VBA это приведет к утечке памяти, потому что сборщик мусора, основанный на подсчете ссылок, никогда не уничтожит объекты с циклическими ссылками, даже если группа, которую они образуют вместе, больше не нужна.

Но что, если вам действительно нужна такая двунаправленная связь?

Существуют разные решения для работы с циклическими ссылками с использованием разных методов. В VBA вам нужен механизм для освобождения перекрестных ссылок в каком-то месте, когда вы знаете, что они больше не нужны. Другими словами, вам нужно как-то управлять самим временем жизни ссылок.

Возможной реализацией может быть поддержка глобального словаря групп, которые активны. Когда вы хотите удалить группу, скажем, в какой-то процедуре, существующей где-то в коде, вы сначала освобождаете (очищаете) ее набор студентов. Это разорвет циклическую ссылку, позволяя сборщику мусора работать правильно. Ниже приведен скелет такой возможной реализации.

'In class clsUser:
  Public id as String '<~~ could be any type of identifier, such as Integer
  Public groups as New Dictionary
  Public Sub subscribeToGroup(g as clsGroup)
      Me.groups.add g.id, g
  End Sub

  Public Sub unsubscribeFromGroup(g as clsGroup)
      Me.groups.Remove g.id
  End Sub
''''''''''''''''''''''''''''''''''''''''''''''''

'In class clsGroup:
  Public id as String '<~~ could be any type of identifier, such as Integer
  Public users as New Dictionary

  Public Sub registerUser(u as clsUser)
      Me.users.add u.id, u
  End Sub

  Public Sub unregisterUser(u as clsUser)
      Me.users.Remove u.id
  End Sub
  Public Sub removeAllUsers() 'empty the users collection, break the circular reference
    For Each u In users: u.unsubscribeFromGroup(Me): Next
    users.RemoveAll
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''

У вас есть где-то (например, глобальная переменная или в каком-то классе с именем GroupManager и т. д.) словарь групп и методы для создания или удаления группы.

Public AllGroups as Dictionary
Function addGroup(id as String) as clsGroup
   Set addGroup = new clsGroup
   addGroup.id = id
   AllGroups.Add id, addGroup
End Function 

Sub removeGroup(g as clsGroup)
    ' Explicitly Tell the group to free its users before going away.
    ' This would not be needed if we didn't have circular references!
    g.removeAllUsers
    AllGroups.Remove g.id
End Sub

Теперь циклические ссылки исчезли, и группа будет уничтожена (без утечки памяти), потому что ничто не содержит ссылок на нее.

person A.S.H    schedule 23.11.2015