środa, sierpień 26, 2009

WPF - Tips & Trics - Freezable

Dziś mały tips, który wpadł mi w oczy przeglądając zasoby MSDN'u.

Aplikacje napisane w WPF (a zwłaszcza animacje) często są postrzegane jako powolne a fakt ten jest przypisany powolności samego silnika WPF. Oczywiście, w pewnym stopniu jest to prawda, ale czasem jest to spowodowane niezrozumieniem WPF'a.

W Windows Presentation Foundation, część obiektów dziedziczy po klasie Freezable. Można by się zastanawiać po cóż nam kolejny poziom dziedziczenia. Jeśli jednak zobaczymy jakie są to klasy, od razu na język przyjdzie nam jedno słowo - Animacje.

Obiekty dziedziczące po Freezable możemy zamrozić sprawiając, iż nie będą one podlegać animacjom, przyśpieszając te elementy, które do animacji są niezbędne.

W zasadzie do tej pory nie napisałem niczego nowego. Nowością (dla mnie) jest fakt, że można zamrozić obiekt z poziomu XAML'a. Jak to zrobić - poniżej.


<Window.Resources>


    <SolidColorBrush x:Key="blueBrush" Color="Blue" PresentationOptions:Freeze="true" />


</Window.Resources>


Aby to zadziałało, musimy dodać odpowiedni namespace:


xmlns:PresentationOptions="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"



Jeśli więc macie niewydajną animację napisaną w WPF, zobaczcie czy nie da się pewnych jej elementów zamrozić a tym samym poprawić wydajność całości.

środa, sierpień 19, 2009

Teoria spiskowa w świecie .NET - Wycieki

Może temat trochę na wyrost, ale już wyjaśniam o co chodzi. Czym jest że tak teoria spiskowa w .NET. Dla mnie jest to dziwne przeświadczenie dużej liczby programistów, że skoro w .NET istnieje Garbage Collector to pojęcie Memory Leaków nie istnieje. Nie chodzmi mi tu o zasoby rzadządzalne, które już znaczna część programistów wie, że należy zwalniać (wołając Dispose, bądź używając klauzuli using), ale o te zarządzalne. Jak to zatem możliwe, możesz spytać?

Garbage Collector to bardzo użyteczne "stworzenie", ale to my jesteśmy "Twórcą" i GC może nam tylko służyć. Nie ma (jeszcze) wbudowanej sztucznej inteligencji, tak więc nie może domyślić się "co autor miał na myśli". Rozpatrzmy sobie takie oto prosty program.


public Form1()


{


    int[] mem = new int[100000];


    for (int i = 0; i < 100000; i++)


    {


        mem[i] = new Random().Next();


    }


    InitializeComponent();


}



private Form1 childForm;


private void button1_Click(object sender, EventArgs e)


{


    childForm = new Form1();           


    childForm.ShowDialog();


    childForm = null;


}   


 


private void button2_Click(object sender, EventArgs e)


{


    GC.Collect();


}



Proste okno z dwoma przyciskami. Pierwszy z nich otwiera okno potomne. Drugi dla uwidocznienia działania wywołuje GC.Collect().

Zobaczmy jak będzie wyglądała pamięć naszego programu po 5-cio krotnym kliknięciu przycisku button1 a następnie oczyszczeniu pamięci przez GC (kliknij obrazek, aby zobaczyć powiększenie).

Widzimy dokładnie to czego się spodziewaliśmy. 5 instancji formy zostało zniszczonych. Zatem wszystko w porządku, tak? Nie do końca. Dodajmy coś co w aplikacjach .NET pojawia się nader często. Eventy.

Dodamy sobie do naszego prostego projektu klasę ustawień. Zrobimy z niej Singleton'a i dodamy możliwość podpięcia się pod zdarzenia, gdy nastąpi zmiana tych właściwości, tak aby formy mogły na nie zareagować.


public class Preferences


{


    private Preferences() {}


 


    private static Preferences instance;


    public static Preferences Instance


    {


        get


        {


            if (instance == null)


                instance = new Preferences();


            return instance;


        }


    }


 


    public void Notify()


    {


        var propertyChnaged = ProprtyChanged;


        if (propertyChnaged == null) return;


        propertyChnaged(this, EventArgs.Empty);


    }


 


    public event EventHandler ProprtyChanged;


}


A w konstruktorze formy dodajmy następującą linię:


Preferences.Instance.ProprtyChanged += Instance_ProprtyChanged;


Uruchommy naszą aplikację pod profiler'em i sprawdźmy jak się zachowa (kliknij obrazek, aby zobaczyć powiększenie).


Widzimy, że nasze formy przetrwały GC. Jak to możliwe? Dzieje się tak dlatego, że obiekt Preferences istnieje cały czas w aplikacji. I dobrze takie jego zadanie, jednak poprzez podpięcie się do niego eventem blokujemy przed GC jego usunięcie.

Rozwiązanie jest proste. Odpięcie tego eventu, gdy zamykamy formę. Proste, a często zapominane przez programistów. Na pewno widzicie pełno kodu, gdzie występuje tylko += a nie ma żadnego -=. Czasami może nam się upiec, nie zawsze występują takie warunki, że forma będzie blokowana przed GC. Jednak zawsze powinniśmy eventy odpisać. To powinien być taki nasz dobry nawyk.

Na zakończenie, zrzut z profiler'a z dodanym odpięciem zdarzenia (kliknij obrazek, aby zobaczyć powiększenie).

Wszystko znów w normie.

Znacie może jakieś inne fałszywe "prawdy" o .NET?

środa, sierpień 05, 2009

Kończę z dotnetomaniakiem....

...spokojnie :), bo kończę jedynie o nim pisać i tyczy się to tylko tego bloga. Strona dorobiła się swojego własnego:

A po cóż taki krok?
Powód jest prosty. Mam nadzieję, że nie tylko ja będę na tamtym blogu pisał. Zapraszam do odwiedzania bloga. Drobne zmiany na maniaku się szykują tak więc niedługo coś się tam pojawi.