2015-10-09 2 views
4

В моих проектах я часто использую таблицы и базовые объекты ListObjects и ListColumns. Мне они нравятся, так как их легче ссылаться и обновлять, чем объекты диапазона Range. Тем не менее, я до сих пор не нашел разумного и удобного способа обработки нескольких ListObjects, состоящих из многих ListColumns, и ссылки на все рабочие листы в проекте.Как написать чистый и поддерживаемый код при работе со столами?

Предположим, у меня есть рабочий лист (с (Name) свойство, установленным в «WorksheetA»), который содержит таблицу (называемую таблицей A) с несколькими столбцами (называемыми столбцами Column1, Column2, ..., Column10).

Теперь я хочу ссылаться на один из столбцов кода другого рабочего листа. Я мог бы сделать это следующим образом:

WorksheetA.ListObjects("TableA").ListColumns("Column7") 

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

Так что же теперь?

Я мог бы создать выделенный модуль для хранения моей строки в качестве констант. Например, модуль называется "Константы":

Public Const TABLE_A As String = "TableA" 
Public Const COLUMN7 As String = "Column7" 

Тогда мое обращение может быть преобразовано в:

WorksheetA.ListObjects(Constants.TABLE_A).ListColumns(Constants.COLUMN7) 

Однако это решение имеет некоторые недостатки:

  1. Константы модуль будет расти до смешного быстро с каждой добавленной таблицей и столбцом.
  2. Ссылка сама по себе растет и становится менее читаемой.
  3. Все константы, связанные со столами из всех книг, бросаются в одну гигантскую яму.

Я мог бы хранить константы внутри WorksheetA, и сделать их доступными через публичные функции, такие как:

Private Const TABLE_A As String = "TableA" 
Private Const COLUMN7 As String = "Column7" 

Public Function GetTableAName() As String 
    GetTableAName = TABLE_A 
End Function 

Public Function GetTableA() As ListObject 
    Set GetTableA = WorksheetA.ListObjects(TABLE_A) 
End Function 

Public Function GetTableAColumn7() As ListColumn 
    Set GetTableAColumn7 = GetTableA().ListColumns(COLUMN7) 
End Function 

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

У вас есть идея, как справиться с этой проблемой?

EDIT1 (для наглядности): Предположим, что пользователь не должен изменять имена (ни имена таблиц, ни имена столбцов). Если пользователь делает это, он виноват.

EDIT2 (для четкости): Я использовал колонку 7 в качестве имени столбца только в качестве примера. Предположим, что столбцы имеют более значимые имена.

+0

Интересная проблема. Если бы было простое решение для этого, я бы предположил, что мы бы увидели его в библиотеках доступа к данным, таких как DAO, ADO, JDBC и т. Д. Единственное, что разработчики могут сделать для инкапсуляции этих «имен полей» и «имен таблиц», писать «постоянные» классы, т. е. один класс для каждой таблицы или объекта данных. Это «чистое» решение, но оно добавляет слой и требует много времени. Он рекомендуется для больших проектов БД, но я сомневаюсь, что проект Excel достигнет такого масштаба, что это решение станет действительно необходимым. –

+0

Я думаю, вы обеспокоены тем, что в строчном подходе имена таблиц могут быть изменены пользователем и, следовательно, нарушают ссылку. Если на листе есть только одна таблица, вы можете использовать номер индекса (1) вместо строки. Это допустимо только для максимальной таблицы на листе. Не знаю, если это применимо на всех ваших листах. Это было бы в вашем примере. –

+0

Если имена столбцов на самом деле такие же неописуемые, как 'Column7', то почему бы не ссылаться на них по числовому индексу? –

ответ

1

Вот мои два цента. Я не образованный программист, но мне платят за это, поэтому я думаю, что это делает меня профессионалом.

Первая линия защиты - это то, что я создаю класс для моделирования таблицы. Я заполняю класс из таблицы, и никакой другой код даже не знает, где живут данные.Когда я инициализации, я буду запускать код как

clsEmployees.FillFromListObject wshEmployees.ListObjects(1) 

Тогда в классе, код выглядит как

vaData = lo.DataBodyRange.Value 
... 
clsEmployee.EeName = vaData(i,1) 
clsEmployee.Ssn = vaData(i,2) 
etc 

Только один ListObject за лист. Это мое правило, и я никогда не нарушаю его. Любой, у кого есть доступ к рабочему листу, может изменить порядок столбцов и сломать мой код. Если я хочу использовать Excel в качестве базы данных, а иногда и так, то это риск, который я беру. Если это так важно, что я не могу принять такой риск, то я храню свои данные в SQL Server, SQLite или JET.

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

Если мой проект достаточно мал или должен работать непосредственно с ListObjects, я следую тем же правилам, что и для любых строк.

  • Я использую строки в коде ровно один раз.
  • Если я использую его более чем один раз, я делаю процедуру уровень постоянного
  • Если я использую его в более чем одной процедуре, я стараюсь передать его в качестве аргумента
  • Если я не могу передать его как аргумент, я делаю константу на уровне модуля
  • Если две процедуры находятся в разных модулях, я сначала спрашиваю себя, почему две процедуры находятся в разных модулях, которые используют одну и ту же константу. Не должны ли связанные процедуры быть в одном модуле?
  • Если две процедуры действительно принадлежат к разным модулям, я пытаюсь передать их в качестве аргумента
  • Если это не работает, то это действительно глобальная константа, и я настроен в моем модуле MGlobals.

Если MGlobals занимает более половины экрана, я делаю что-то неправильно, и мне нужно отступить и продумать то, что я пытаюсь выполнить. И затем я создаю собственный класс.

Смежные вопросы