Оптимизация производительности: Поведение объекта
Понимание внутреннего поведения объектов WPF поможет найти оптимальное сочетание функциональных возможностей и производительности.
Не удаление обработчиков событий для объектов может поддерживать объекты в активном состоянии
Делегат, который объект передает в свое событие, фактически является ссылкой на этот объект. Таким образом, обработчики событий могут поддерживать объекты в активном состоянии дольше, чем планировалось. При выполнении очистки объекта, зарегистрированного для прослушивания события объекта, необходимо удалить этот делегат перед освобождением объекта. Сохранение ненужных объектов в активном состоянии увеличивает потребление памяти. Это особенно важно в тех случаях, когда объект является корневым элементом логического дерева или визуального дерева.
WPF предоставляет шаблон прослушивателя слабых событий для событий, которые могут быть полезны в ситуациях, когда трудно отслеживать отношения между источником и прослушивателем во время существования объекта. Некоторые существующие события WPF используют этот шаблон. При реализации объектов с пользовательскими событиями этот шаблон может вам пригодиться. Дополнительные сведения см. в разделе Шаблоны слабых событий.
Существует несколько инструментов, таких как профилировщик CLR и Working Set Viewer, которые могут предоставлять сведения об использовании памяти указанным процессом. Профилировщик CLR включает ряд очень полезных представлений профиля выделения, включая гистограмму выделенных типов, диаграммы выделения и вызова, временную шкалу, показывающую сборку мусора разных поколений и итоговое состояние управляемой кучи после этих сборок, а также дерево вызовов, показывающее распределения по методам и загрузки сборок. Дополнительные сведения см. в разделе Центр разработчиков .NET Framework.
Свойства и объекты зависимостей
Как правило, доступ к свойству зависимостей из DependencyObject не медленнее, чем CLR свойство. Хотя существуют небольшие издержки производительности при задании значения свойства, получение значения происходит так же быстро, как получение значения из свойства CLR. Издержки производительности компенсируются за счет того, что свойства зависимостей поддерживают надежные функции, такие как привязка данных, анимация, наследование и задание стилей. Дополнительные сведения см. в обзоре свойств зависимостей.
Оптимизация DependencyProperty
Свойства зависимостей в приложении следует определять очень внимательно. Если ваш DependencyProperty например затрагивает только отображение параметров метаданных типа, а не другие параметры метаданных AffectsMeasure, следует пометить его как таковое путем переопределения его метаданных. Дополнительные сведения о переопределении и получении метаданных свойства см. в разделе Метаданные свойств зависимостей.
Возможно, более рационально использовать обработчик изменений свойств, чтобы аннулировать передачу размера, упорядочения и отображения вручную, если не все изменения свойств фактически влияют на размер, упорядочение и отображение. Например, вы можете решить заново отображать фон, только когда значение выше установленного ограничения. В этом случае обработчик изменений свойств будет аннулировать отображение, только когда значение превышает установленное ограничение.
Наследуемость DependencyProperty не обходится даром
По умолчанию зарегистрированные свойства зависимостей не являются наследуемыми. Тем не менее вы можете явно сделать любое свойство наследуемым. Хотя это полезная возможность, преобразование свойства в наследуемое негативно влияет на производительность, увеличивая интервал времени для аннулирования свойства.
Используйте RegisterClassHandler с осторожностью
Во время вызова методов RegisterClassHandler позволяет сохранить состояние экземпляра, важно Имейте в виду, что обработчик вызывается в каждом экземпляре, что может вызвать проблемы производительности. Использовать только RegisterClassHandler Если вашему приложению требуется сохранять состояние экземпляра.
Установка значения по умолчанию для DependencyProperty во время регистрации
При создании DependencyProperty , должен иметь значение по умолчанию, задайте значение с помощью метаданных по умолчанию, передаваемый в качестве параметра для Register метод DependencyProperty. Рекомендуется использовать именно этот метод вместо настройки значения свойства в конструкторе или в каждом экземпляре элемента.
Установка значения PropertyMetadata с помощью реестра
При создании DependencyProperty, у вас есть возможность задания PropertyMetadata одним Register или OverrideMetadata методы. Несмотря на то, что объект может иметь статический конструктор для вызова OverrideMetadata, это не является оптимальным решением и повлияет на производительность. Для наилучшей производительности значение PropertyMetadata во время вызова Register.
Объекты Freezable
Объект Freezable — это специальный тип объекта, имеющий два состояния: фиксированное и нефиксированное. Фиксация объектов везде, где это возможно, улучшает производительность приложения и уменьшает его рабочий набор. Дополнительные сведения см. в разделе Общие сведения об объектах класса Freezable.
Каждый Freezable имеет Changed событие, возникающее при каждом его изменении. Однако уведомления об изменениях обходятся дорого с точки зрения производительности приложения.
Рассмотрим следующий пример, в котором каждый Rectangle использует тот же Brush объекта:
rectangle_1.Fill = myBrush;
rectangle_2.Fill = myBrush;
rectangle_3.Fill = myBrush;
// ...
rectangle_10.Fill = myBrush;
По умолчанию WPF предоставляет обработчик событий для SolidColorBrush объекта Changed событий, чтобы сделать недействительным Rectangle объекта Fill свойство. В этом случае каждый раз, когда SolidColorBrush приходится запускать его Changed событий, необходимо вызывать функцию обратного вызова для каждого Rectangle— совокупность этих вызовов функции обратного вызова накладывают к значительному снижению производительности. Кроме того, довольно затратно с точки зрения производительности добавлять и удалять обработчики на этом этапе, так как приложению потребуется пройти по всему списку, чтобы сделать это. Если сценарий приложения никогда не меняется SolidColorBrush, вы будете нести затраты на поддержку Changed обработчики событий без необходимости.
Замораживание Freezable позволяет повысить его производительность, поскольку он больше не требуется тратить ресурсы на уведомления об изменениях. В следующей таблице показан размер простого объекта SolidColorBrush при его IsFrozen свойству true
, если он недопустим по сравнению с. Это предполагает применение одной кисти к Fill свойство десяти Rectangle объектов.
Регион | Размер |
---|---|
FROZEN SolidColorBrush | 212 байт |
Нефиксированный SolidColorBrush | 972 байта |
Следующий пример кода демонстрирует эту концепцию.
Brush frozenBrush = new SolidColorBrush(Colors.Blue);
frozenBrush.Freeze();
Brush nonFrozenBrush = new SolidColorBrush(Colors.Blue);
for (int i = 0; i < 10; i++)
{
// Create a Rectangle using a non-frozed Brush.
Rectangle rectangleNonFrozen = new Rectangle();
rectangleNonFrozen.Fill = nonFrozenBrush;
// Create a Rectangle using a frozed Brush.
Rectangle rectangleFrozen = new Rectangle();
rectangleFrozen.Fill = frozenBrush;
}
Обработчики событий изменений Changed в нефиксированных объектах Freezable могут поддерживать объекты в активном состоянии
Делегат, который объект передает в Freezable объекта Changed событий является ссылкой на этот объект. Таким образом Changed обработчики событий могут поддерживать объекты в активном состоянии больше, чем ожидалось. При выполнении очистки объекта, зарегистрированного для прослушивания Freezable объекта Changed событий, очень важно удалить этот делегат перед освобождением объекта.
WPF также подключает Changed события внутренним образом. Например, все свойства зависимостей принимающие Freezable как значение будет прослушивать Changed события автоматически. Fill Свойство, которое принимает Brush, иллюстрирует эту концепцию.
Brush myBrush = new SolidColorBrush(Colors.Red);
Rectangle myRectangle = new Rectangle();
myRectangle.Fill = myBrush;
При назначении myBrush
для myRectangle.Fill
делегат, указывающий обратно Rectangle добавляется объект SolidColorBrush объекта Changed событий. Это означает, что следующий код в действительности не разрешает myRect
сборку мусора.
myRectangle = null;
В этом случае myBrush
по-прежнему сохраняет myRectangle
активном состоянии и снова вызовет его при его срабатывании его Changed событий. Обратите внимание, что при назначении myBrush
для Fill свойства нового Rectangle будет просто добавляться другой обработчик событий в myBrush
.
Рекомендуемый способ очистки этих типов объектов должно быть удаление Brush из Fill свойство, которое в свою очередь приведет к удалению Changed обработчик событий.
myRectangle.Fill = null;
myRectangle = null;
Виртуализация пользовательского интерфейса
WPF также предоставляет разновидность StackPanel элемент, который автоматически «виртуализирует» привязкой к данным дочернего содержимого. В данном контексте слово «виртуализация» означает способ, с помощью которого подмножество объектов создается из большего количества элементов данных в зависимости от того, какие из элементов отображаются на экране. Как для памяти, так и для процессора затратно создавать большое число элементов пользовательского интерфейса, при том что только несколько из них могут отображаться на экране одновременно. VirtualizingStackPanel (с помощью функций, предоставляемых VirtualizingPanel) подсчитывает видимые элементы и работает с ItemContainerGenerator из ItemsControl (такие как ListBox или ListView) создавать только элементы для видимых элементов.
Для оптимизации производительности визуальные объекты для этих элементов создаются или поддерживаются в активном состоянии, только если они будут отображаться на экране. Если они больше не находятся в видимой области элемента управления, визуальные объекты могут быть удалены. Это не следует путать с виртуализацией данных, где не все объекты данных присутствуют в локальной коллекции, а скорее, передаются в потоке при необходимости.
В следующей таблице показано затраченное время, добавление и отрисовку 5000 TextBlock элементы StackPanel и VirtualizingStackPanel. В этом случае измеряется время между присоединением текстовой строки для ItemsSource свойство ItemsControl и временем, когда элементы panel отображением этой текстовой строки.
Главная панель | Время отрисовки (мс) |
---|---|
StackPanel | 3210 |
VirtualizingStackPanel | 46 |