Использование переданной строки для доступа к другим свойствам класса

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

Контекст: Воспроизведение элиты: опасно. У меня есть список звездных систем на листе Excel с каждой строкой, содержащей одну систему (столбцы: имя, x, y, z координаты и некоторые свойства, такие как Посещенные, RareGoodsSource). Я создал класс StarSystem и прочитал рабочий лист в коллекции StarSystems (с именем colSys). Это работает. Для каждого свойства класса у меня есть отдельный лист (columns: name, property), где я вручную изменяю значения свойств (например, только что посетил Tau Ceti в игре, на листе «csvVisited» вручную добавить строку «Tau Ceti», «TRUE») , В VBA я затем сравниваю их со значениями в элементах коллекции и при необходимости обновляю их. (В конце концов я перекачаю все это в AutoCAD, чтобы визуализировать и планировать маршруты поездок.)

Проблема. В настоящее время у меня есть отдельный Sub для каждого свойства, идентичный, за исключением имени рабочего листа (например, «csv Посещенные » / «csv RareGoodsSource ») и ссылки для доступа к свойству (например, colSys.Item (r.Value). Посещенные / colSys.Item (r.Value). RareGoodsSource ). Это работает. Но это кажется неправильным с точки зрения эстетики, эффективности и обслуживания. Конечно, у меня должен быть только один Sub, который я передаю посетителю или RareGoodsSource по мере необходимости?

Мой текущий код для этого универсального субподрядчика находится в конце сообщения, во-первых, для ясности я имею крайне абстрагированную версию. Моя первая попытка состояла в том, чтобы просто буквально заменить Browse с strProperty всюду в Sub и передать гостевой или RareGoodsSource в Sub в эту строковую переменную.

Это отлично работает для ссылки на рабочий лист, по-видимому, потому что .Item () требует строки в любом случае. Я не совсем удивлен, что это не работает для ссылки на свойство, потому что я передаю строчную переменную в надежде, что VBA понимает это как имя свойства объекта, но мне не удалось найти, как это сделать. Надеюсь, это просто результат моей неловкой нехватки базовых знаний о программировании, и мне просто нужны некоторые скобки или цитаты или «где-то».

Упрощенный пример кода, который работает правильно (… кроме бита, который этого не делает, очевидно):

Sub TestVisited() Call TestGeneric("Visited") End Sub Sub TestGeneric(strProperty As String) Dim wsCSV As Worksheet Set wsCSV = ActiveWorkbook.Worksheets.Item("csv" & strProperty) 'successfully sets wsCSV to Worksheets.Item("csvVisited"), 'presumably because .Item() expects a string anyway. Dim r As Range For Each r In wsCSV.Range(wsCSV.Cells(2, 1), wsCSV.Cells(4, 1)) Debug.Print "Explicitly coded: " & colSys.Item(r.Value).Visited Debug.Print "Passed as string: " & colSys.Item(r.Value).strProperty Next r 'The first Debug.Print works, the second does not: '"Object doesn't support this property or method." End Sub 

Текущий реальный код для контекста:

(Примечание. Я отключил ловушку ошибок при замене .Contains, потому что в противном случае это могло бы помешать этой проблеме.)

 Sub UpdatePropertyFromWorksheetCSVProperty(strProperty As String) 'set the cell column/row positions in Worksheets. Let celCSVDataColumn = 2 'prepare reference to Worksheet to read. Dim wsCSV As Worksheet Set wsCSV = ActiveWorkbook.Worksheets.Item("csv" & strProperty) 'prepare reference to Range to read. Dim rngData As Range Set rngData = wsCSV.Range(wsCSV.Cells(celFirstDataRow, celKeyColumn), wsCSV.Cells( _ wsCSV.Cells(wsCSV.Rows.Count, celKeyColumn).End(xlUp).Row _ , celKeyColumn)) ' middle segment finds the last occupied cell in column A and returns its row index. 'for each Worksheet row, compare the property value in the Worksheet to the value in the Collection Element, 'if different write the Worksheet value to the Collection Element, and flag the Element as ModifiedSinceRead. Dim r As Range For Each r In rngData 'check Sytem exists in the Collection. 'except VBA Collections don't have a .Contains method apparently. 'use error trapping instead. 'On Error GoTo ErrorHandler 'compare/copy Worksheet and Collection values. If Not colSys.Item(r.Value).strProperty = r.Offset(0, celCSVDataColumn - 1).Value Then On Error GoTo 0 'disables error trap again. Let colSys.Item(r.Value).strProperty = r.Offset(0, celCSVDataColumn - 1).Value Let colSys.Item(r.Value).xlsModifiedSinceRead = True 'DEBUG: test to immediate window Debug.Print "System " & colSys.Item(r.Value).Name & " " & strProperty & " property changed to " & colSys.Item(r.Value).strProperty & "." ' End If ResumeNextSystem: Next r 'DEBUG: test to immediate window Debug.Print colSys(1).Name & vbTab & colSys(1).x & vbTab & colSys(1).RareGoodsSource & vbTab & colSys(1).RareGoodsChecked & vbTab & colSys(1).Visited & vbTab & colSys(1).xlsModifiedSinceRead Debug.Print colSys(10160).Name & vbTab & colSys(10160).x & vbTab & colSys(10160).RareGoodsSource & vbTab & colSys(10160).RareGoodsChecked & vbTab & colSys(10160).Visited & vbTab & colSys(10160).xlsModifiedSinceRead Debug.Print colSys("Lave").Name & vbTab & colSys("Lave").x & vbTab & colSys("Lave").RareGoodsSource & vbTab & colSys("Lave").RareGoodsChecked & vbTab & colSys("Lave").Visited & vbTab & colSys("Lave").xlsModifiedSinceRead ' Exit Sub ErrorHandler: MsgBox ("Processing Worksheet " & wsCSV.Name & " error at system " & r.Value & ", skipping to next.") 'DEBUG: test to immediate window Debug.Print "Processing Worksheet " & wsCSV.Name & " error at system " & r.Value & ", skipping to next." ' Resume ResumeNextSystem End Sub 

Решение в реальном коде:

 'stays as-is: Set wsCSV = ActiveWorkbook.Worksheets.Item("csv" & strProperty) 'Get old: If Not colSys.Item(r.Value).strProperty = r.Offset(0, celCSVDataColumn - 1).Value Then 'new: If Not CallByName(colSys.Item(r.Value), strProperty, VbGet) = r.Offset(0, celCSVDataColumn - 1).Value Then 'Let old: Let colSys.Item(r.Value).strProperty = r.Offset(0, celCSVDataColumn - 1).Value 'new: CallByName colSys.Item(r.Value), strProperty, VbLet, r.Offset(0, celCSVDataColumn - 1).Value 

Вы можете использовать встроенную функцию CallByName для получения свойства.

 v = CallByName(colSys.Item(r.Value), strProperty, vbGet) 

Эта статья в KB объясняет это: https://support.microsoft.com/kb/186143

Interesting Posts

Доступ к коду VBA 2016 для экспорта в Excel, «не удалось найти устанавливаемый ISAM»

Поддерживать диаграмму phpexcel

Excel – Как условно форматировать ячейку с временным форматом, когда его время составляет один час до текущего времени?

Чтение большого файла Excel .xlsx

Я продолжаю получать System.Runtime.InteropServices.COMException (0x80028018): старый формат или недопустимая библиотека типов. ошибка

Как суммировать неследующие ячейки в шаблоне jxls

Получение максимального номера строки / столбца из диапазона Excel

Excel. Отметьте ячейку «X», если документ существует на основе сводной таблицы

Как изменить первый столбец в текстовый формат в SQL на XLS?

Получить максимальное количество символов, которые могут содержать ячейки

Цитирование через электронную таблицу Excel (с использованием openpyxl)

Как я могу убрать пустые строки в файле Excel с помощью OleDB?

Как проверить, пуста ли ячейка даты в Excel?

Использование коллекций в Access VBA

Макрос VBA, который циклически меняет имена листов

Давайте будем гением компьютера.