Compartir a través de


Optimización del rendimiento: comportamiento de objetos

Comprender el comportamiento intrínseco de los objetos WPF le ayudará a compensar las ventajas adecuadas entre la funcionalidad y el rendimiento.

No eliminar los gestores de eventos en los objetos puede mantener activos los objetos

El delegado que un objeto pasa a su evento es efectivamente una referencia a ese objeto. Por lo tanto, los controladores de eventos pueden mantener los objetos activos durante más tiempo de lo esperado. Al realizar la limpieza de un objeto que se ha registrado para escuchar los eventos de otro objeto, es esencial quitar dicho delegado antes de liberar el objeto. Mantener activos los objetos innecesarios aumenta el uso de memoria de la aplicación. Esto es especialmente cierto cuando el objeto es la raíz de un árbol lógico o un árbol visual.

WPF presenta un patrón de escucha de eventos débil para eventos que pueden ser útiles en situaciones en las que las relaciones de ciclo de vida de los objetos entre el origen y el escucha son difíciles de seguir. Algunos eventos de WPF existentes usan este patrón. Si va a implementar objetos con eventos personalizados, este patrón puede ser de su uso. Para obtener más información, consulte Patrones de eventos débiles.

Hay varias herramientas, como CLR Profiler y el Visor de conjuntos de trabajo, que pueden proporcionar información sobre el uso de memoria de un proceso especificado. CLR Profiler incluye una serie de vistas muy útiles del perfil de asignación, incluido un histograma de tipos asignados, gráficos de asignación y llamadas, una línea de tiempo que muestra recolecciones de basura de varias generaciones y el estado resultante del montón administrado después de esas recolecciones, y un árbol de llamadas que muestra asignaciones por método y cargas de ensamblaje. Para obtener más información, consulte Rendimiento.

Propiedades y objetos de dependencia

En general, el acceso a una propiedad de dependencia de un DependencyObject no es más lento que el acceso a una propiedad CLR. Aunque hay una leve sobrecarga de rendimiento al establecer un valor de propiedad, obtener un valor es tan rápido como obtener el valor de una propiedad CLR. Compensar la pequeña sobrecarga de rendimiento es el hecho de que las propiedades de dependencia proporcionan características robustas, como el enlace de datos, la animación, la herencia y el estilo. Para obtener más información, consulte Información general sobre las propiedades de dependencia.

Optimizaciones de DependencyProperty

Debe definir las propiedades de dependencia en la aplicación con mucho cuidado. DependencyProperty Si solo afecta a las opciones de metadatos de tipo de representación, en lugar de otras opciones de metadatos, como AffectsMeasure, debe marcarla como tal invalidando sus metadatos. Para obtener más información sobre cómo invalidar o obtener metadatos de propiedad, vea Metadatos de propiedad de dependencia.

Puede ser más eficiente que un controlador de cambio de propiedad invalide manualmente los procesos de medida, disposición y representación si no todos los cambios de propiedad afectan realmente estos procesos. Por ejemplo, puede decidir volver a representar un fondo solo cuando un valor es mayor que un límite establecido. En este caso, el controlador de cambios de propiedad solo invalidaría la representación cuando el valor supera el límite establecido.

Hacer que DependencyProperty sea heredable no es gratis

De forma predeterminada, las propiedades de dependencia registradas no se pueden heredar. Sin embargo, puede hacer explícitamente que cualquier propiedad sea heredable. Aunque esta es una característica útil, la conversión de una propiedad para que se pueda heredar afecta al rendimiento aumentando el período de tiempo para la invalidación de propiedades.

Usa RegisterClassHandler con cuidado

Aunque la llamada RegisterClassHandler permite guardar el estado de la instancia, es importante tener en cuenta que se llama al controlador en cada instancia, lo que puede causar problemas de rendimiento. RegisterClassHandler Use solo cuando la aplicación requiera guardar el estado de la instancia.

Establecer el valor predeterminado de dependencyProperty durante el registro

Al crear un DependencyProperty que requiere un valor predeterminado, establezca el valor mediante los metadatos predeterminados pasados como parámetro al Register método de DependencyProperty. Use esta técnica en lugar de establecer el valor de propiedad en un constructor o en cada instancia de un elemento.

Establecer el valor PropertyMetadata mediante Register

Al crear un DependencyProperty, tiene la opción de establecer el PropertyMetadata utilizando los métodos Register o OverrideMetadata. Aunque tu objeto podría tener un constructor estático para llamar a OverrideMetadata, no es la solución óptima y afectará al rendimiento. Para obtener el mejor rendimiento, establezca el PropertyMetadata durante la llamada a Register.

Objetos congelables

A Freezable es un tipo especial de objeto que tiene dos estados: descongelado y congelado. Congelar objetos siempre que sea posible mejora el rendimiento de tu aplicación y reduce su conjunto de trabajo. Para obtener más información, consulte Información general sobre objetos congelables.

Cada Freezable tiene un Changed evento que se genera cada vez que cambia. Sin embargo, las notificaciones de cambio son costosas en términos de rendimiento de la aplicación.

Tenga en cuenta el ejemplo siguiente en el que cada uno Rectangle usa el mismo Brush objeto:

rectangle_1.Fill = myBrush;
rectangle_2.Fill = myBrush;
rectangle_3.Fill = myBrush;
// ...
rectangle_10.Fill = myBrush;
rectangle_1.Fill = myBrush
rectangle_2.Fill = myBrush
rectangle_3.Fill = myBrush
' ...
rectangle_10.Fill = myBrush

De forma predeterminada, WPF proporciona un controlador de eventos para el SolidColorBrush evento del Changed objeto para invalidar la Rectangle propiedad del Fill objeto. En este caso, cada vez que SolidColorBrush tenga que desencadenar su evento Changed, es necesario invocar la función de devolución de llamada para cada Rectangle. La acumulación de estas invocaciones de función de devolución de llamada impone una penalización considerable en el rendimiento. Además, agregar y quitar controladores en este punto requiere mucho rendimiento, ya que la aplicación tendría que recorrer toda la lista para hacerlo. Si el escenario de la aplicación nunca cambia SolidColorBrush, pagará innecesariamente el costo de mantener Changed controladores de eventos.

La congelación de un Freezable puede mejorar su rendimiento, ya que ya no necesita gastar recursos para mantener las notificaciones de cambio. En la siguiente tabla se muestra el tamaño de un simple SolidColorBrush cuando su propiedad IsFrozen se establece en true, en comparación con cuando no está establecida. Esto supone aplicar un pincel a la Fill propiedad de diez Rectangle objetos.

Estado Tamaño
Congelado SolidColorBrush 212 bytes
No congelado SolidColorBrush 972 bytes

En el ejemplo de código siguiente se muestra este concepto:

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;
}
Dim frozenBrush As Brush = New SolidColorBrush(Colors.Blue)
frozenBrush.Freeze()
Dim nonFrozenBrush As Brush = New SolidColorBrush(Colors.Blue)

For i As Integer = 0 To 9
    ' Create a Rectangle using a non-frozed Brush.
    Dim rectangleNonFrozen As New Rectangle()
    rectangleNonFrozen.Fill = nonFrozenBrush

    ' Create a Rectangle using a frozed Brush.
    Dim rectangleFrozen As New Rectangle()
    rectangleFrozen.Fill = frozenBrush
Next i

Los manejadores modificados en freezables no congelados pueden mantener activos los objetos.

El delegado que un objeto pasa al evento Freezable de un objeto Changed es efectivamente una referencia a ese objeto. Por lo tanto, Changed los controladores de eventos pueden mantener los objetos activos durante más tiempo de lo esperado. Al realizar la limpieza de un objeto que se ha registrado para escuchar un evento de objeto FreezableChanged, es esencial quitar ese delegado antes de liberar el objeto.

WPF también enlaza eventos Changed internamente. Por ejemplo, todas las propiedades de dependencia que toman Freezable como valor escucharán eventos de Changed automáticamente. La Fill propiedad, que toma un Brush, ilustra este concepto.

Brush myBrush = new SolidColorBrush(Colors.Red);
Rectangle myRectangle = new Rectangle();
myRectangle.Fill = myBrush;
Dim myBrush As Brush = New SolidColorBrush(Colors.Red)
Dim myRectangle As New Rectangle()
myRectangle.Fill = myBrush

En la asignación de myBrush a myRectangle.Fill, se agregará un delegado que apunte de vuelta al objeto Rectangle en el evento SolidColorBrush del objeto Changed. Esto significa que el código siguiente no es myRect apto para la recolección de elementos no utilizados:

myRectangle = null;
myRectangle = Nothing

En este caso myBrush sigue manteniendose myRectangle activo y volverá a llamar a él cuando active su Changed evento. Tenga en cuenta que asignar myBrush a la propiedad Fill de un nuevo Rectangle simplemente agregará otro controlador de eventos a myBrush.

La manera recomendada de limpiar estos tipos de objetos es quitar la Brush de la propiedad Fill, que a su vez quitará el controlador de eventos Changed.

myRectangle.Fill = null;
myRectangle = null;
myRectangle.Fill = Nothing
myRectangle = Nothing

Virtualización de la interfaz de usuario

WPF también proporciona una variación del StackPanel elemento que "virtualiza" automáticamente el contenido secundario enlazado a datos. En este contexto, la palabra virtualizar hace referencia a una técnica por la que se genera un subconjunto de objetos a partir de un mayor número de elementos de datos en función de qué elementos están visibles en pantalla. Es intensivo, tanto en términos de memoria como de procesador, para generar un gran número de elementos de interfaz de usuario cuando solo unos pocos pueden estar en la pantalla en un momento dado. VirtualizingStackPanel (a través de la funcionalidad proporcionada por VirtualizingPanel) calcula los elementos visibles y trabaja con el ItemContainerGenerator de un ItemsControl (como ListBox o ListView) para crear solo elementos para los artículos visibles.

Como optimización del rendimiento, los objetos visuales de estos elementos solo se generan o mantienen activos si están visibles en la pantalla. Cuando ya no están en el área visible del control, se pueden quitar los objetos visuales. Esto no se debe confundir con la virtualización de datos, donde los objetos de datos no están presentes en la colección local, sino que se transmiten según sea necesario.

En la tabla siguiente se muestra el tiempo transcurrido agregando y representando 5000 TextBlock elementos en y StackPanel .VirtualizingStackPanel En este escenario, las medidas representan el tiempo entre adjuntar una cadena de texto a la ItemsSource propiedad de un ItemsControl objeto a la hora en que los elementos del panel muestran la cadena de texto.

Panel host Tiempo de representación (ms)
StackPanel 3210
VirtualizingStackPanel 46

Consulte también