Udostępnij za pośrednictwem


Optymalizacja wydajności: zachowanie obiektu

Zrozumienie wewnętrznego zachowania obiektów WPF pomoże Ci dokonać odpowiednich kompromisów między funkcjonalnością a wydajnością.

Nie usuwaj programów obsługi zdarzeń w obiektach może zachować żywość obiektów

Delegat, który obiekt przekazuje do jego zdarzenia, jest w rzeczywistości odwołaniem do tego obiektu. W związku z tym programy obsługi zdarzeń mogą utrzymywać obiekty aktywne dłużej niż oczekiwano. Podczas czyszczenia obiektu, który zarejestrował się w celu nasłuchiwania zdarzenia obiektu, niezbędne jest usunięcie tego delegata przed zwolnieniem obiektu. Utrzymywanie niepotrzebnych obiektów przy życiu zwiększa użycie pamięci aplikacji. Jest to szczególnie istotne, gdy obiekt jest katalogiem głównym drzewa logicznego lub drzewa wizualnego.

WPF wprowadza słaby wzorzec odbiornika zdarzeń, które mogą być przydatne w sytuacjach, gdy relacje okresu istnienia obiektu między źródłem i odbiornikiem są trudne do śledzenia. Niektóre istniejące zdarzenia WPF używają tego wzorca. W przypadku implementowania obiektów ze zdarzeniami niestandardowymi ten wzorzec może być używany. Aby uzyskać szczegółowe informacje, zobacz Słabe wzorce zdarzeń.

Istnieje kilka narzędzi, takich jak CLR Profiler i Podgląd zestawu roboczego, które mogą zawierać informacje na temat użycia pamięci określonego procesu. Profiler CLR zawiera wiele bardzo przydatnych widoków profilu alokacji, w tym histogram przydzielonych typów, alokacji i wywołań grafów, linię czasową pokazującą odzyskiwanie pamięci różnych generacji oraz wynikowy stan sterty zarządzanej po tych kolekcjach oraz drzewo wywołań przedstawiające alokacje poszczególnych metod i obciążenia zestawów. Aby uzyskać więcej informacji, zobacz Wydajność.

Właściwości i obiekty zależności

Ogólnie rzecz biorąc, uzyskiwanie dostępu do właściwości zależności elementu DependencyObject nie jest wolniejsze niż uzyskiwanie dostępu do właściwości CLR. Chociaż istnieje niewielkie obciążenie związane z wydajnością ustawiania wartości właściwości, uzyskanie wartości jest tak szybkie, jak pobieranie wartości z właściwości CLR. Obniżenie obciążenia związanego z małą wydajnością polega na tym, że właściwości zależności obsługują niezawodne funkcje, takie jak powiązanie danych, animacja, dziedziczenie i stylizacja. Aby uzyskać więcej informacji, zobacz Właściwości zależności — omówienie.

Optymalizacje zależnościwłaściwości

Właściwości zależności w aplikacji należy definiować bardzo ostrożnie. DependencyProperty Jeśli dotyczy tylko opcji metadanych typu renderowania, a nie innych opcji metadanych, takich jak AffectsMeasure, należy oznaczyć je jako takie, przesłaniając jego metadane. Aby uzyskać więcej informacji na temat zastępowania lub uzyskiwania metadanych właściwości, zobacz Metadane właściwości zależności.

Bardziej wydajne może być, aby program obsługi zmiany właściwości unieważnił miarę, rozmieścić i renderować przechodzi ręcznie, jeśli nie wszystkie zmiany właściwości rzeczywiście wpływają na miarę, rozmieszczanie i renderowanie. Na przykład możesz zdecydować się na ponowne renderowanie tła tylko wtedy, gdy wartość jest większa niż ustawiony limit. W takim przypadku procedura obsługi zmiany właściwości unieważniłaby renderowanie tylko wtedy, gdy wartość przekroczy ustawiony limit.

Tworzenie właściwości DependencyProperty Dziedziczone nie jest bezpłatne

Domyślnie zarejestrowane właściwości zależności nie są dziedziczone. Można jednak jawnie ustawić dowolną właściwość, którą można dziedziczyć. Chociaż jest to przydatna funkcja, konwertowanie właściwości w celu dziedziczenia ma wpływ na wydajność przez zwiększenie czasu unieważnienia właściwości.

Ostrożnie używaj programu RegisterClassHandler

Wywołanie RegisterClassHandler umożliwia zapisanie stanu wystąpienia, należy pamiętać, że program obsługi jest wywoływany w każdym wystąpieniu, co może powodować problemy z wydajnością. Używaj RegisterClassHandler tylko wtedy, gdy aplikacja wymaga zapisania stanu wystąpienia.

Ustawianie wartości domyślnej dla właściwości DependencyProperty podczas rejestracji

Podczas tworzenia obiektu DependencyProperty , który wymaga wartości domyślnej, ustaw wartość przy użyciu domyślnych metadanych przekazanych jako parametr do Register metody DependencyProperty. Użyj tej techniki zamiast ustawiać wartość właściwości w konstruktorze lub w każdym wystąpieniu elementu.

Ustawianie wartości PropertyMetadata przy użyciu funkcji Register

Podczas tworzenia obiektu DependencyPropertymożesz ustawić PropertyMetadata metodę Register przy użyciu metod lub OverrideMetadata . Chociaż obiekt może mieć konstruktor statyczny do wywołania OverrideMetadata, nie jest to optymalne rozwiązanie i będzie miało wpływ na wydajność. Aby uzyskać najlepszą PropertyMetadata wydajność, ustaw parametr podczas wywołania na Register.

Obiekty z możliwością zamrażania

A Freezable jest specjalnym typem obiektu, który ma dwa stany: niezamrozzone i zamrożone. Zamrażanie obiektów zawsze, gdy jest to możliwe, poprawia wydajność aplikacji i zmniejsza jej zestaw roboczy. Aby uzyskać więcej informacji, zobacz Omówienie obiektów z możliwością zamrażania.

Każdy Freezable z nich ma zdarzenie Changed , które jest zgłaszane za każdym razem, gdy się zmienia. Jednak powiadomienia o zmianach są kosztowne pod względem wydajności aplikacji.

Rozważmy następujący przykład, w którym każdy z nich Rectangle używa tego samego Brush obiektu:

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

Domyślnie WPF udostępnia procedurę obsługi zdarzeń dla SolidColorBrush zdarzenia obiektu Changed w celu unieważnienia RectangleFill właściwości obiektu. W takim przypadku za każdym razem, gdy SolidColorBrush konieczne jest wyzwolenie zdarzenia Changed , wymagane jest wywołanie funkcji wywołania zwrotnego dla każdego Rectangle— akumulacja tych wywołań funkcji wywołania zwrotnego nakłada znaczną karę za wydajność. Ponadto bardzo intensywnie korzysta z wydajności, aby dodawać i usuwać programy obsługi, ponieważ aplikacja musiałaby przejść przez całą listę, aby to zrobić. Jeśli scenariusz aplikacji nigdy nie zmienia SolidColorBrushwartości , niepotrzebnie płacisz koszt obsługi Changed zdarzeń.

Zamrożenie elementu Freezable może zwiększyć wydajność, ponieważ nie musi już wydać zasobów na konserwowanie powiadomień o zmianach. W poniższej tabeli przedstawiono rozmiar prostego SolidColorBrush , gdy jego IsFrozen właściwość jest ustawiona na true, w porównaniu z wartością , gdy nie jest. Przyjęto założenie, że stosuje się jedną szczotkę Fill do właściwości dziesięciu Rectangle obiektów.

Stanowy Rozmiar
Mrożone SolidColorBrush 212 Bajty
Niemarznięte SolidColorBrush 972 Bajty

W poniższym przykładzie kodu przedstawiono tę koncepcję:

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

Zmienione programy obsługi w niezamrozzonych zamrażaniach mogą zachować aktywne obiekty

Delegat, który obiekt przekazuje do Freezable zdarzenia obiektu Changed , jest w rzeczywistości odwołaniem do tego obiektu. W związku z Changed tym programy obsługi zdarzeń mogą utrzymywać obiekty aktywne dłużej niż oczekiwano. Podczas czyszczenia obiektu, który zarejestrował się w Freezable celu nasłuchiwania zdarzenia obiektu Changed , niezbędne jest usunięcie tego delegata przed zwolnieniem obiektu.

WPF również podłącza Changed zdarzenia wewnętrznie. Na przykład wszystkie właściwości zależności, które przyjmują Freezable jako wartość, będą nasłuchiwać Changed zdarzeń automatycznie. Właściwość Fill , która przyjmuje Brushelement , ilustruje tę koncepcję.

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

W przypisaniu do obiektu element delegowany wskazujący obiekt zostanie dodany do RectangleSolidColorBrush zdarzenia obiektuChanged.myRectangle.FillmyBrush Oznacza to, że następujący kod nie kwalifikuje myRect się do odzyskiwania pamięci:

myRectangle = null;
myRectangle = Nothing

W tym przypadku myBrush nadal żyje myRectangle i odwołuje się do niego, gdy uruchamia swoje Changed zdarzenie. Należy pamiętać, że przypisanie myBrush do Fill właściwości nowego Rectangle programu obsługi zdarzeń spowoduje po prostu dodanie kolejnej procedury obsługi zdarzeń do myBrushklasy .

Zalecanym sposobem wyczyszczenia tych typów obiektów jest usunięcie Brush obiektu z Fill właściwości, która z kolei usunie Changed program obsługi zdarzeń.

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

Wirtualizacja interfejsu użytkownika

WPF udostępnia również odmianę StackPanel elementu, który automatycznie "wirtualizuje" powiązaną zawartość podrzędną danych. W tym kontekście słowo wirtualizowanie odnosi się do techniki, za pomocą której podzbiór obiektów jest generowany na podstawie większej liczby elementów danych na podstawie elementów widocznych na ekranie. Jest to intensywne, zarówno pod względem pamięci, jak i procesora, generowanie dużej liczby elementów interfejsu użytkownika, gdy tylko kilka może znajdować się na ekranie w danym momencie. VirtualizingStackPanel (za pośrednictwem funkcji udostępnianych przez VirtualizingPanelprogram ) oblicza widoczne elementy i współpracuje z elementem ItemContainerGenerator z ItemsControl elementu (na przykład ListBox lub ListView), aby tworzyć tylko elementy dla widocznych elementów.

W ramach optymalizacji wydajności obiekty wizualne dla tych elementów są generowane lub przechowywane jako aktywne tylko wtedy, gdy są widoczne na ekranie. Gdy nie znajdują się już w widoku kontrolki, obiekty wizualne mogą zostać usunięte. Nie należy tego mylić z wirtualizacją danych, gdzie obiekty danych nie są obecne w lokalnej kolekcji— raczej przesyłane strumieniowo w razie potrzeby.

W poniższej tabeli przedstawiono czas, który upłynął, dodając i renderujący 5000 TextBlock elementów do elementu StackPanel i VirtualizingStackPanel. W tym scenariuszu miary reprezentują czas między dołączeniem ciągu tekstowego do ItemsSource właściwości ItemsControl obiektu a czasem wyświetlania ciągu tekstowego przez elementy panelu.

Panel hosta Czas renderowania (ms)
StackPanel 3210
VirtualizingStackPanel 46

Zobacz też