Сегодня столкнулся с ситуацией когда приведение FilteredElementCollector к List, используя метод расширения ToList(), ускорил работу плагина раз эдак в 500!
Но обо всем по порядку.
Начиная изучать Revit API и плохо зная все основы Net я натолкнулся на статью
Перечисление элементов. Топик немного о другом конечно и я на него не наговариваю. Но дело в том, что в мозгу он как-то отложился и запомнился как "всегда делай итерацию по FilteredElementCollector без предварительного приведения типов". Однако, есть ситуации, когда так делать нельзя и сейчас расскажу пример такой ситуации.
Задача простая - перебираем все листы в документе и получаем данные со штампов (TitleBlock). Вот пример кода, который я использовал изначально:
private void GetDataFromSheets(Document doc)
{
// Получаем листы в документе
IList<Element> viewSheetElements = new FilteredElementCollector(doc).OfClass(typeof(ViewSheet)).ToElements();
// Получаем все штампы
FilteredElementCollector titleBlocks = new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_TitleBlocks);
// делаем итерацию по листам
foreach (var element in viewSheetElements)
{
var viewSheetElement = (ViewSheet) element;
if (!viewSheetElement.IsTemplate && viewSheetElement.ViewType == ViewType.DrawingSheet)
{
GetDataFromTitleBlocks(viewSheetElement, titleBlocks);
}
}
}
private void GetDataFromTitleBlocks(ViewSheet viewSheet, FilteredElementCollector titleBlocks)
{
foreach (Element element in titleBlocks)
{
if (element.OwnerViewId.IntegerValue != viewSheet.Id.IntegerValue) continue;
if (element is FamilyInstance familyInstance)
{
// some code
}
}
}
Т.е. для ускорения процесса я сначала получил все TitleBlock в документе, а затем в методе
GetDataFromTitleBlocks просто сравниваю OwnerViewId с Id листа. Так было сделано специально, так как при получении FilteredElementCollector для конкретного вида вызывалась перерисовка графики на виде.
Как видите, в методе
GetDataFromTitleBlocks ничего не происходит. Если вы запустите этот код с вызовом метода
GetDataFromSheets в достаточно пустом документе, то ничего не заметите. Но если запустить этот код в реальном проекте, где уже есть пару сотен листов с кучей видов на них, то вот этот
пустой код будет работать очень долго!
Почему так? Я не сразу это понял. И даже когда мне объяснили, то до сих пор не понимаю))
Все дело в том, что FilteredElementCollector реализует интерфейс IEnumerable<Element>. При перечислении FilteredElementCollector для каждого элемента скорее всего вызываются какие-то методы, а в моем примере выше такая итерация происходит для каждого листа в документе. Возможно мое "объяснение" не совсем верное, поэтому вот ссылка на статью на Хабре -
Проблемы использования IEnumerable. И еще стоит прочитать про такое предупреждение, выдаваемое
Решарпером -
Possible Multiple Enumeration of IEnumerable.
К сожалению в моем коде решарпер не может уловить такую разновидность предупреждения и предательски молчит.
Как это лечится - а лечится это очень просто - использованием List вместо FilteredElementCollector. Вношу небольшие изменения в начальный код:
private void GetDataFromSheets(Document doc)
{
// Получаем листы в документе
IList<Element> viewSheetElements = new FilteredElementCollector(doc).OfClass(typeof(ViewSheet)).ToElements();
// Получаем все штампы
List<Element> titleBlocks = new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_TitleBlocks).ToList();
// делаем итерацию по листам
foreach (var element in viewSheetElements)
{
var viewSheetElement = (ViewSheet) element;
if (!viewSheetElement.IsTemplate && viewSheetElement.ViewType == ViewType.DrawingSheet)
{
GetDataFromTitleBlocks(viewSheetElement, titleBlocks);
}
}
}
private void GetDataFromTitleBlocks(ViewSheet viewSheet, List<Element> titleBlocks)
{
foreach (Element element in titleBlocks)
{
if (element.OwnerViewId.IntegerValue != viewSheet.Id.IntegerValue) continue;
if (element is FamilyInstance familyInstance)
{
// some code
}
}
}
Пробую - и ура - код срабатывает моментально!
Ко всему вышесказанному вспоминается высказывание (точно не скажу откуда) "Если не знаете что использовать, то используйте List"!
Надеюсь мой случай окажется полезным и ком-нибудь спасет нервы и время ))