Назначение объектов в VB6
Я пытаюсь создать два идентичных объекта в VB6 с помощью операторов присваивания; что-то вроде этого...
Dim myobj1 As Class1
Dim myobj2 As Class1
Set myobj1 = New Class1
myobj1.myval = 1
Set myobj2 = myobj1
Стало очевидным, что это не создает два объекта, а скорее две ссылки на один и тот же объект, что не то, что я хочу. Есть ли способ создать второй объект таким образом, или мне нужно скопировать объект по одному члену за раз...
Set myobj2 = new Class1
myobj2.mem1 = myobj1.mem1
...
?
Изменить 2 Скотт Уитлок обновил свой отличный ответ, и я включил его изменения в этот теперь действующий фрагмент кода.
Private Type MyMemento
Value1 As Integer
Value2 As String
End Type
Private Memento As MyMemento
Public Property Let myval(ByVal newval As Integer)
Memento.Value1 = newval
End Property
Public Property Get myval() As Integer
myval = Memento.Value1
End Property
Friend Property Let SetMemento(new_memento As MyMemento)
Memento = new_memento
End Property
Public Function Copy() As Class1
Dim Result As Class1
Set Result = New Class1
Result.SetMemento = Memento
Set Copy = Result
End Function
Затем выполняется назначение в коде таким образом...
Set mysecondobj = myfirstobj.Copy
Ответы
Ответ 1
Как и многие современные языки, VB6 имеет типы значений и ссылочные типы. Классы определяют ссылочные типы. С другой стороны, ваши основные типы, такие как Integer
, являются типами значений.
Основное отличие заключается в назначении:
Dim a as Integer
Dim b as Integer
a = 2
b = a
a = 1
В результате a
равно 1 и b
равно 2. Это потому, что присваивание в типах значений создает копию. Это потому, что каждая переменная имеет пространство, выделенное для значения в стеке (в случае VB6, Integer занимает 2 байта в стеке).
Для классов он работает по-другому:
Dim a as MyClass
Dim b as MyClass
Set a = New MyClass
a.Value1 = 2
Set b = a
a.Value1 = 1
В результате оба a.Value1
и b.Value1
равны 1. Это потому, что состояние объекта хранится в куче, а не в стеке. В стек хранится только ссылка на объект, поэтому Set b = a
перезаписывает ссылку. Интересно, что VB6 явно говорит об этом, заставляя использовать ключевое слово Set
. Большинство других современных языков не требуют этого.
Теперь вы можете создавать свои собственные типы значений (в VB6 они называются User Defined Types, но на большинстве других языков они называются структурами или структурами). Здесь tutorial.
Различия между классом и определенным пользователем типом (кроме класса, являющегося ссылочным типом и UDT, являющимся типом значения) состоит в том, что класс может содержать поведение (методы и свойства), где UDT не может. Если вы просто ищете класс типа записи, то UDT может быть вашим решением.
Вы можете использовать сочетание этих методов. Скажем, вам нужен класс, потому что у вас есть определенные поведения и вычисления, которые вы хотите включить вместе с данными. Вы можете использовать memment pattern, чтобы удерживать состояние объекта внутри UDT:
Type MyMemento
Value1 As Integer
Value2 As String
End Type
В своем классе убедитесь, что все ваше внутреннее состояние хранится внутри частного члена типа MyMemento
. Напишите свои свойства и методы, чтобы они использовали данные только в одной переменной частного члена.
Теперь сделать копию вашего объекта просто. Просто напишите новый метод в своем классе под названием Copy()
, который возвращает новый экземпляр вашего класса и инициализирует его копией собственного сообщения:
Private Memento As MyMemento
Friend Sub SetMemento(NewMemento As MyMemento)
Memento = NewMemento
End Sub
Public Function Copy() as MyClass
Dim Result as MyClass
Set Result = new MyClass
Call Result.SetMemento(Memento)
Set Copy = Result
End Function
Friend
только скрывает его от материала вне вашего проекта, поэтому он не сильно скрывает подтекст SetMemento
, но все, что вы можете сделать с VB6.
НТН
Ответ 2
@Scott Whitlock, я не смог заставить ваш код работать, но если он будет работать, это будет здорово.
Я создал регулярный модуль, где я помещаю тип memment
Type MyMemento
Value1 As Integer
Value2 As String
End Type
Затем я создаю модуль класса MyClass с кодом
Private Memento As MyMemento
Friend Sub SetMemento(NewMemento As MyMemento)
Memento = NewMemento
End Sub
Public Function Copy() as MyClass
Dim Result as MyClass
Set Result = new MyClass
Result.SetMemento(Memento)
Set Copy = Result
End Function
Наконец, я пытаюсь вызвать функцию копирования в другом регулярном модуле, таком как
Sub Pruebas()
Dim Primero As MyClass, segundo As MyClass
Set Primero = New MyClass
Set segundo = New MyClass
Set segundo = Primero.Copy
End Sub
Я получаю сообщение (под рисунком): Ошибка компиляции: El tipo de agumento de ByRef не совпадает
Вот изображение (не более 10 баллов, так вот ссылка): http://i.stack.imgur.com/KPdBR.gif
Я не смог получить сообщение на английском языке, я живу в Испании.
Не могли бы вы предоставить пример в VBA Excel? Я действительно пытался сделать эту работу.
Спасибо за вашу работу
===============================================
РЕДАКТИРОВАТЬ: проблема решена:
Проблема была в строке "Result.SetMemento(Memento)", в VBA ей нужно было вызвать "Call"
Public Function Copy() As MyClass
Dim Result As MyClass
Set Result = New MyClass
Call Result.SetMemento(Memento)
Set Copy = Result
End Function
Он отлично работает, благодаря Скотту Уитлоку, вы гений.
Ответ 3
или мне нужно скопировать объект по одному члену за раз...
К сожалению, да.
Возможно (но технически очень сложно) написать COM-сервер на С++, который - с помощью интерфейса IDispatch - скопирует значение каждого свойства, но на самом деле это программирование с высоким храмом, если бы мне пришлось это делать, Я не знаю, смогу ли я это сделать, но я бы посмотрел на что-то вроде 10-дневной работы (и я знаю, как COM реализован на С++, мне также нужно исследовать, чтобы увидеть, имеет ли инфраструктура ATL что-либо помощь и т.д.).
Я работал с Vb3, 4,5 и 6 для чего-то вроде 10 лет (в течение 5 дней в неделю) и никогда не нашел хорошего способа сделать это, помимо ручного внедрения шаблонов сериализации, таких как Mementos и Save and Store, которые действительно просто сводились к фантастическим способам копирования каждого члена, по одному за раз.