Dim AList As New ArrayList() Public Structure a Dim Check As Windows.Forms.CheckState Dim Name As String End Structure
Dim c1 As a c1.Check = CheckState.Checked c1.Name = "1" AList.Add(c1)
'Читает вроде нормально msgbox(AList(0).name) 'А вот с присвоением не проходит, ПОЧЕМУ? и что делать? AList(0).name = "123"
'Исключение An unhandled exception of type 'System.Exception' occurred in microsoft.visualbasic.dll Additional information: Latebound assignment to a field of value type 'a' is not valid when 'a' is the result of a latebound expression.
Ну, во-первых, надо всегда ставить Option Strict On. Тогда не будет странных ошибок времени выполнения.
Но этот код (с присвоением) всё равно не будет работать как надо. Твоя структура a - значимый тип (value type), в ArrayList'е он хранится в упакованнов (boxed) виде. Для того, чтобы использовать свойство этой структуры, необходимо произвести её распаковку (unboxing). Распаковка происходит при приведении типа AList(0) от Object к a, которое будет ясно видно с включённым Option Strict.
При распаковке в стеке программы будет создана временная копия этой структуры. Любые изменения свойств копии никак не повлияют на само значение в списке. Эта копия будет вскоре удалена. Поскольку свойства копии идентичны свойствам значения в списке, чтение любого свойства работает без проблем.
Тут есть только два решения - либо использовать объект вместо структуры, либо заменять сразу всю структуру. Это можно сделать так: AList(0) = New a("123", CheckState.Checked), если, конечно, написать соответствующий конструтор.
Все конечно хорошо но вот какая проблема. Я заранее не знаю какой тип обекта. То есть я знаю что свойство .Name, а тип мне неизвесный, a CType требует тип непосредственнo. Что делать то есть как передать тип в CType через параметр? Приведенная ниже инструкция не работает
sub(byref AList as ArrayList, TypeObject as Type) ... CType(Alist(0),TypeObject).name="123" ... end sub
если у классов есть свойство Name но нет общего предка (и он не
предполагается) для превидения, то возможно нужно воспользоваться
интерфейсом и приводить к нему, например
Interface Int
property Name as String
end Interface
class class1
implements Int
public property Name implements Int.Name
Set()
Get()
end property
end class
class class2
implements Int
public property Name implements Int.Name
Set()
Get()
end property
end class
ну и потом CType(AList(0), Int).name = "123" не зависимо от типа AList(0)
между прочим объявлять так "Public name As String" в классах немного
противоречит принципам правильного ООП
Public Property Name() As String Get Return theName End Get Set(ByVal value As String) theName = value End Set End Property
Классы не должны иметь публично доступных полей.
Кстати, я согласен, что все эти CType - это . Но пока приходится либо писать свою коллекцию, либо всё время писать CType (кстати, DirectCast должен работать быстрее). Только в VB 2005 это сделают почеловечески.
Да получается, спасибо всем очень помогли. Замечание:
1. Если интерфейс обявляется не в одном модуле то надо указывать Public Interface Int что бы потом(в моем случае интерфейс и клас его использующий находятся в разних проектах) иметь к нему доступ, а то, как я понял, по умолчанию там Private.
2. У меня так не идет: public property Name implements Int.Name а идет так: Public Property Name() As String Implements Int.Name
3. На щёт правильного ООП то я понял что надо через property ноиногда лень матушка писать лишний код.
А лишнего кода здесь и нет , это всё окупится. Кстати, тебя никто и
не заставляет это писать вручную. Я обычно генерю представление класса
из объявлений вида:
Name: String
SomeProperty: SomeType
скриптом на perl'е. Но вовсе не обязательно изучать перл, есть
наверняка куча готовых утилит, которые из одной строчки всё делают
(плагинов к VS, в первую очередь). Мне сейчас просто лень искать.