Słowo kluczowe global

Dziś trochę dla odmiany, nie będę pisał o wielowątkowości a o jednym z podstawowych słów kluczowych w c#, które na szczęście nie jest bardzo popularne – bazując na moim doświadczeniu z różnymi projektami, rzadko widuje je w kodzie.

Global przydaje się gdy mamy konflikty z przestrzeniami nazw. Wyobraźmy sobie, że mamy następujący kod (kod  bazuje na dokumentacji MSDN):

class Program
{
    public class System { }
    const int Console = 7;

    static void Main()
    {
    }
}

Następnie chcemy w metodzie Console wyświetlić jakąś wartość. Na początku próbujemy użyć po prostu Console.WriteLine:

class Program
{
    public class System { }
    const int Console = 7;

    static void Main()
    {
        Console.WriteLine("Hello World"); // Konflikt
    }
}

Niestety mamy konflikt ze stałą Console i kod nie skompiluje się. Próbujemy więc następnie użyć System.Console.WriteLine:

class Program
{
    public class System { }
    const int Console = 7;

    static void Main()
    {
        System.Console.WriteLine("Hello World"); //Znów konflikt
    }
}

Niestety znów mamy konflikt… Tym razem problem powoduje zagnieżdżona klasa System.  System. nie odnosi się do namespace ale właśnie do tej zagnieżdżonej klasy. Rozwiązaniem błędnego projektu przestrzeni nazw jest użycie słówka global:

class Program
{
    public class System
    {
    }
    const int Console = 7;

    static void Main()
    {
        global::System.Console.WriteLine("Hello World");
    }
}

Global nakazuje poszukiwanie typów od miejsca startowego (globalnego) a nie lokalnego, zależnego od aktualnego kontekstu. Oczywiście nie należy deklarować przestrzeni nazw, które pokrywają się z systemowymi dostarczonymi przez .NET Framework – jest to zła praktyka. Jeśli jednak nie mamy na to wpływu wtedy global  może okazać się bardzo przydatnym rozwiązaniem.

Code review: Wielowątkowy singleton

Kiedyś na blogu opisywałem kilka implementacji singleton’a przystosowanych do pracy w środowisku wielowątkowym. Jedna z nich opierała się na tzw. double checked locking:

public sealed class Singleton
{
    private static Singleton m_Instance = null;
    private static readonly object m_Sync = new object();

    private Singleton() { }

    public static Singleton Instance
    {
        get
        {
            if(m_Instance == null )
            {
                lock(m_Sync)
                {
                    if(m_Instance == null)
                        m_Instance = new Signleton();
                }
            }
            return m_Instance;
        }
    }
}

Niestety implementacja nie jest w pełni poprawna. Brakuje w niej modyfikatora volatile na polu m_Instance. Jeśli nie jest dla Was zrozumiałe co robi słówko volatile zachęcam do przeczytania mojego wpisu o tym. Bez tego ciężko będzie zrozumieć dlaczego volatile jest konieczny. Poprawna implementacja wygląda więc następująco:

public sealed class Singleton
{
    private static volatile Singleton m_Instance = null;
    private static readonly object m_Sync = new object();

    private Singleton() { }

    public static Singleton Instance
    {
        get
        {
            if(m_Instance == null )
            {
                lock(m_Sync)
                {
                    if(m_Instance == null)
                        m_Instance = new Signleton();
                }
            }
            return m_Instance;
        }
    }
}

Z wpisu o volatile wiemy, że rdzenie w zależności od zaimplementowanego modelu pamięci, mogą wykonywać cache na polach typu read. Pole m_Instance  zalicza się również do tego przykładu. W przypadku gdy jeden wątek stworzy nową instancję m_Instance, drugi wątek nie koniecznie zauważy tą zmianę. Pierwszy IF ( if(m_instance == null) jest niebezpieczny ponieważ istnieje ryzyko, że rdzeń aktualnie ma tą wartość w cache i zawsze warunek może być spełniony – mimo, że już w drugim wątku instancja została utworzona.  Jeśli warunek jest spełniony wtedy zostanie wywołany lock, który z kolei wykona flush i następny warunek ( if(m_Instance==null)) nie zostanie spełniony, jednak niepotrzebnie zostanie nałożona blokada.

Innymi słowy, volatile jest potrzebny wyłącznie dla pierwszego if’a aby nie było konieczności zakładania bez potrzeby blokady.

W następnych postach zajmiemy się tzw. MemoryBarrier, który ma podobne zastosowanie do volatile i wiąże się ściśle z architekturą procesora oraz dokonywanymi optymalizacjami.

Obiekty Freezable

WPF dostarcza obiekt Freezable. Możemy dziedziczyć po tej klasie aby stworzyć własne obiekty immutable. Ze względów wydajnościowych, można takowy obiekt “zamrozić” i wtedy nie można już go więcej modyfikować – staje się więc immutable.  Przykład:

internal class SampleClass : Freezable
{
   public SampleClass(string text)
   {
       Text = text;
   }

   protected override Freezable CreateInstanceCore()
   {
       return new SampleClass(null);
   }

   public static readonly DependencyProperty TextProperty =
       DependencyProperty.Register("Text", typeof (string), typeof (SampleClass));

   public string Text
   {
       get { return this.GetValue(TextProperty) as string; }
       set { this.SetValue(TextProperty, value); }
   }
}

Przykład nie jest zbyt skomplikowany. Dziedziczymy po Freezable i implementujemy CreateInstanceCore, która będzie służyć do klonowania obiektów ( o tym za chwilę). Aby zamrozić obiekt wystarczy wywołać metodę Freeze:

var sampleClass = new SampleClass("tekst");
sampleClass.Freeze();
sampleClass.Text = "Nie da rady...";

Po zamrożeniu, próba modyfikacji na właściwości Text zakończy się wyjątkiem InvalidOperationException: “Cannot set a property on object ‘WpfApplication2.SampleClass’ because it is in a read-only state.” Freezable sam dba o zabronienie dostępu do właściwości pod warunkiem, że są one typu DependencyProperties. W innym przypadku sprawa wygląda bardziej skomplikowanie. Wiemy, że próba zakończy się wyjątkiem zatem bezpieczniej w kodzie sprawdzać w jakim stanie jest aktualnie obiekt:

if(sampleClass.IsFrozen==false)
    sampleClass.Text = "Value";

Obiekt raz zamrożony nie może zostać odblokowany. Jedyna możliwość to sklonowanie jego np:

var sampleClass = new SampleClass("tekst");
if(sampleClass.CanFreeze)
    sampleClass.Freeze();

var clonedObject = (SampleClass)sampleClass.Clone();
MessageBox.Show(clonedObject.IsFrozen.ToString());// po sklonowaniu, ponownie false
MessageBox.Show(clonedObject.Text);

Clone wykonuje głęboką kopie obiektu. Zaimplementowana prze nas metoda CreateInstanceCore tworzy jedynie instancję obiektu – wszystkie właściwości zostaną sklonowane za nas automatycznie. CreateInstnaceCore ogranicza się zwykle do wywołania domyślnego konstruktora – reszta zostanie wykonana przez Freezable.

Freezable dostarcza również zdarzenie Changed, przydatne podczas monitorowania stanu obiektu:

private void Button_Click(object sender, RoutedEventArgs e)
{
  var sampleClass = new SampleClass("tekst");
  sampleClass.Changed += new EventHandler(SampleClassChanged);
  sampleClass.Text = "Zmiana tekstu";
}
void SampleClassChanged(object sender, EventArgs e)
{
}

Jakie ma to praktyczne zastosowanie? Klasa Freezable została dodana głównie ze względów wydajnościowych. Zamrożony obiekt nie zmieni swojego stanu zatem nie musimy go obserwować. Na przykład kontrolki w WPF muszą być przerenderowane gdy któryś z obiektów zmienił swój stan. Wspomniane wcześniej zdarzenie Changed nie będzie musiało już być monitorowane. Klasy zamrożone są oczywiste bezpieczne z punktu widzenia wielowątkowości ponieważ mamy pewność, że właściwości nie zostaną zmodyfikowane z innego wątku. Dzięki temu, nie trzeba używać czasochłonnych blokad (lock) lub innych metod synchronizacji.

Freezable jest powszechny w kontrolkach WPF. Na przykład zdarzenie Brush.Changed  jest śledzone i w momencie jego wywołania, kontrolka jest przerenderowywana z nowym kolorem. Inne przykłady to klasy Transform , Geometry czy animacje. Wszystkie pędzle systemowe są domyślnie zamrożone. Nie możliwe jest na przykład zmiana koloru dla jakiekolwiek systemowego pędzla:

SolidColorBrush systemBrush = Brushes.White;
MessageBox.Show(systemBrush.IsFrozen.ToString()); // zawsze true

SolidColorBrush customBrush=new SolidColorBrush(Colors.AliceBlue);
MessageBox.Show(customBrush.IsFrozen.ToString()); // domyslnie false

Istnieje również możliwość zamrożenia obiektu prosto z poziomu XAML. Załóżmy, że definiujemy Brush w zasobach:

<LinearGradientBrush ice:Freeze="True" 
xmlns:ice="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options" 
/>

Dobre i złe praktyki w C# – część I

Na moim blogu co jakiś czas można znaleźć informacje dotyczące pisania dobrego oraz złego kodu w c#. Oprócz tego zacząłem pisać serie artykułów związanych z tą tematyką. Wiele informacji w tych artykułach można było znaleźć już wcześniej na moim blogu ale myślę, że ten cykl stanowi dobre dopełnienie i podsumowanie tego wszystkiego co piszę tutaj. Oczywiście artykuły rozszerzają tematykę, oraz zawierają więcej przykładów więc tym bardziej zachęcam do lektury.

Dziś została opublikowana pierwsza część:

http://msdn.microsoft.com/pl-pl/library/dobre-i-zle-praktyki-w-c-sharp–czesc-1.aspx

Metryka TypeRank

Dziś czas na kolejną metrykę badania kodu  – TypeRank.

Wartość metryki jest liczona za pomocą algorytmu Google Page Rank, który jest wykorzystywany przez wyszukiwarki do indeksowania stron. W świecie wyszukiwarek internetowych im większa wartość tym strona znajdzie się wyżej podczas prezentowania wyników. Algorytm bada powiązania między stronami internetowymi. Strona dostaje wyższą ocenę, gdy więcej innych stron wskazuje na nią. Stwierdzono, że skoro do danej strony odwołuje się więcej innych witryn to prawdopodobnie zawiera ona treść o lepszej jakości.

Przykład:

W sieci istnieją tylko 4 strony internetowe A, B, C, D. Na początku każda posiada więc wartość PageRank równą 0.25 ponieważ uniwersum składa się z 4 elementów a przyjmuje się skale od 0-1. Strony B, C, D zawierają odnośniki do A. Zatem PageRank witryny A wynosi:

image

Jeśli dodatkowo założy się, że B zawiera odnośnik również do C, z kolei D do wszystkich innych (A,B,C), wtedy wartość rankingu jest dzielona przez liczbę odnośników:

image

Innymi słowy strona A jest wyżej w rankingu jeśli strona B zawiera wyłącznie odnośnik do A, a nie jednocześnie do np. A, B, C.

Empirycznie stwierdzono, że PageRank wyliczany na podstawie powyższego wzoru generuje zbyt duże wartości. Z tego względu do wzoru dodano tzw. współczynnik tłumienia, który najczęściej przyjmuje wartość 0.85. Kompletny wzór zatem wygląda następująco:

image

gdzie:

PR – PageRank danej strony,

d – współczynnik tłumienia. Wartość między 0 i 5, najczęściej 0.85,

L – liczba odsyłaczy na danej stronie internetowej,

N – całkowita liczba stron internetowych.

TypeRank działa w identyczny sposób. Jedyną różnicą jest liczenie metryki na podstawie typów (klas, struktur itp.) a nie stron internetowych. W przeciwieństwie do PageRank, wartości duże TypeRank są niepożądane ponieważ oznaczają, że wiele klas jest zależnych od badanego typu. Podczas modyfikacji typów z dużą wartością TypeRank należy uważnie przetestować ponownie system – są one szczególnie wrażliwe na zmiany. Jeśli to możliwe należy rozdzielać typy z wysoką wartością metryki lub przynajmniej unikać zaawansowanej logiki w takich typach.

Code review: Badanie spójności kodu

Dziś trochę o matematycznej walidacji kodu czyli o metrykach.  LCOM (lack  cohesion of methods) umożliwia obliczenie jak bardzo nasze metody są spójne. Do obliczania tej wartości istnieją gotowe narzędzia takie jak nDepend, ale aby w pełni zrozumieć znaczenie metryki obliczymy sami jej wartość dla prostej klasy.

Powinno dążyć się do jak największej spójności a tym samym do jak najniższej wartości LCOM. Przed wyjaśnieniem czym jest metryka LCOM wprowadźmy następujące pojęcia:

M – zbiór metod danej klasy (M1,M2…Mn),

Ii – zbiór zmiennych składowych klasy wykorzystywanych przez metodę Mi.

Wartość LCOM jest równa:

image

Innymi słowy, LCOM ma na celu wyeliminowanie zmiennych składowych, które wykorzystywane są tylko w jednej metodzie. Zmienne składowane, które wywoływane są wyłącznie w jednej metodzie powinny być przeniesione do tej metody i zadeklarowane w jej ciele.

Pełna definicja LCOM wygląda następująco:

image

Rozważny poniższy fragment kodu:

public class ClassA
{
    private int m_Var1 = 0;
    private int m_Var2 = 0;
    private int m_Var3 = 0;
    private int m_Var4 = 0;
    
    void Method1()    
    {    
        m_Var1 = 0;    
        m_Var2 = 1;     
    }    
    void Method2()    
    {    
        m_Var1 = 0;    
        m_Var3 = 4;    
    }    
    void Method3()    
    {    
        m_Var4 = 5;    
    }
}

Na podstawie kodu można wyznaczyć:

I1 = {m_Var1,m_Var2}

I2 = {m_Var1,m_Var3}

I3 = {m_Var4}

Następnie należy wyznaczyć wszystkie kombinacje metod których jest:

image

image

Występują dwa zbiory puste zatem wartość metryki LCOM wynosi dwa.

Na podstawie metryki można stwierdzić, że klasa została źle zaprojektowana. Powinno dążyć do zerowej wartości LCOM poprzez przenoszenie składowanych wykorzystywanych tylko w jednej metodzie do ciała tej metody. Pola składowe powinny być wykorzystywane przez jak największą liczbę metod a najlepiej wszystkie. Powinniśmy unikać sytuacji gdzie jedna metoda korzysta z pól całkowicie innych niż druga metoda tej samej klasy!

Metrykę LCOM można również wyrazić za pomocą:

image

gdzie:

M – liczba metod,

F – liczba pól,

MF – liczba pól wykorzystywanych przez daną metodę. Wyrażenie sum(MF) określa sumę wykorzystywanych pól przez każdą z metod.

Metryka wyliczana na podstawie wzoru przyjmuje wartości między 0 a 1. Wartość 0 oznacza maksymalną spójność, z kolei 1 brak spójności. W praktyce kryterium LCOM ciężko spełnić i dlatego wprowadzono pewną modyfikację o szerszym zasięgu:

image

LCOM HS przyjmuje wartości od 0 do 2. W przypadku wykrycia klasy z LCOM HS > 1.0 należy koniecznie klasę zrefaktoryzować.

Klasa ThreadLocal

W ostatnim poście opisałem atrybut ThreadStatic pozwalający na korzystanie ze statycznych pól w sposób niezależny w każdym wątku. Dziś pora na analogiczną klasę, wprowadzoną w .NET 4.0 – ThreadLocal. Zacznijmy od przykładu:

class ClassA
{    
    private ThreadLocal<int> _value=new ThreadLocal<int>();

    public void RunAsync(object value)
    {
        var thread = new Thread(Run);
        thread.Start(value);
        thread.Join();
    }
    private void Run(object value)
    {
        if(value!=null)
            _value.Value = (int)value;

        Console.WriteLine(_value);
    }
}

Następnie wywołujemy RunAsync:

var classA = new ClassA();
classA.RunAsync(2);
classA.RunAsync(null);      

Bez użycia ThreadLocal, otrzymalibyśmy na ekranie 2,2. Powyższy przykład używa jednak ThreadLocal więc pola są niezależne – na ekranie wyświetli się 2,0.

Należy jednak uważać na wątki z puli. Jak wiemy, wątek z puli po zakończeniu działania jest zwracany z powrotem do puli a nie niszczony. Z tego względu może się okazać, że dwa wątki odpalone jeden po drugim będą współdzielić taką samą wartość- jeden się zakończy i potem ten sam będzie uruchomiony.

ThreadLocal umożliwia jeszcze Lazy Loading. Jeśli wartość nie zostanie jawnie ustawiona wtedy można skorzystać z lazy loading aby ją określić np.:

class ClassA
{
    private ThreadLocal<int> _value = new ThreadLocal<int>(() => 5);

    public void RunAsync(object value)
    {
        var thread = new Thread(Run);
        thread.Start(value);
        thread.Join();
    }
    private void Run(object value)
    {
        if(value!=null)
            _value.Value = (int)value;

        Console.WriteLine(_value);
    }
}

W przypadku gdy _value.Value nie zostało ustawione, wtedy próba wywołania _value.Value zwróci zawsze 5. Stanowi to zaletę w porównaniu do ThreadStatic gdzie nie mogliśmy określić wartości domyślnej. Druga oczywista zaleta to fakt, że ThreadStatic dotyczył wyłącznie statycznych pól.

Atrybut ThreadStaticAttribute

W C# istnieje dość mało popularny atrybut ThreadStaticAttribute. Do czego on służy? Zacznijmy od przykładu:

internal class Program
{
    [ThreadStatic] 
    private static int _value;

    private static void Main(string[] args)
    {
        Thread thread1 = new Thread(PrintValue);
        Thread thread2 = new Thread(PrintValue);
        thread1.Start(4);
        thread1.Join();
        thread2.Start();
    }
    static private void PrintValue(object args)
    {
        if(args!=null)
            _value = (int) args;
        Console.WriteLine(_value);
    }
}

Co się pojawi na wyjściu? Bez atrybutu ThreadStatic byłoby to (4,4). Atrybut jednak mówi, że pole statyczne będzie unikalne dla każdego wątku. Czyli Thread1 będzie miał własną kopię oraz tak samo Thread2. Statyczne pola oznaczone tym atrybutem nie są współdzielone przez wątki. W powyższym przykładzie, pierwszy wątek ustawi wartość na 4 a drugi z kolei będzie miał swoją kopie i wyświetli wartość domyślną czyli 0.

Należy zaznaczyć, że pole ThreadStatic powoduje pewien overhead i czytanie takiej wartości jest dużo wolniejsze (nawet kilkadziesiąt razy). Kiedy używać tego? Szczerze mówiąc rzadko, tak samo jak rzadko należy używać klas statycznych. Jeśli mamy jednak pole statyczne, które jest modyfikowane przez różne wątki, wtedy aby uniknąć kosztownych blokad, warto oznaczyć je ThreadStatic.

Ważna uwaga: nie należy inicjalizować pól statycznych ThreadStatic wartością początkową. Dlaczego? Pomyślmy kiedy wartość początkowa jest wstawiana. Na etapie kompilacji? Nie… Ma to miejsce w momencie pierwszego dostępu do pola a konkretnie kiedy zostanie wywołany konstruktor statyczny. Niestety wartość domyślna będzie widziana tylko dla pierwszego wątku, który chciał uzyskać dostęp:

internal class Program
{
    [ThreadStatic] private static int _value = 54;

    private static void Main(string[] args)
    {
        Console.WriteLine(_value);
        Thread thread1 = new Thread(PrintValue);
        Thread thread2 = new Thread(PrintValue);
        thread1.Start();
        thread1.Join();
        thread2.Start();
    }
    static private void PrintValue(object args)
    {
        Console.WriteLine(_value);
    }
}

Powyższy kod wyświetli wartości 54,0,0. Główny wątek wywoła inicjalizację wartością 54 a następne niestety już tego nie uczynią. Jedynie możemy mieć pewność, że wartość w nowych wątkach będzie wartością domyślną  ( dla liczb zero a dla obiektów NULL).

WeakEventManager w WPF 4.5

W ostatnich postach pisałem o słabych referencjach oraz podałem przykładowe implementacje obsługi zdarzeń. Niestety mimo generycznego rozwiązania, jednej wady nie udało się usunąć a mianowicie tzw. sacrifice object. Przed rozpoczęciem czytania tego wpisu zachęcam  najpierw do zapoznania się z poprzednimi.

WeakEventManager to klasa WPF. Jak wspomniałem, często nie wiemy kiedy listener jest usuwany z pamięci w różnego rodzaju kontrolkach. Z tego względu to właśnie WPF dostarcza odpowiednią klasę. W wersji 4.5 WeakEventManager znacząco został uproszczony i naprawdę niewiele trzeba pisać własnego kodu. Przed pojawieniem się 4.5 również istniała możliwość wykorzystania WeakEventManager ale wiązało to się  z implementacją interfejsu i pewnej klasy bazowej. Nie było to eleganckie ale do najczęściej wykorzystywanych typów zdarzeń (PropertyChanged, ButtonClick) istniały już zaimplementowane managery. Od wersji 4.5 wszystko jest po prostu generyczne.

Przykład:

public class SampleClass : INotifyPropertyChanged
{
   public event PropertyChangedEventHandler PropertyChanged;
   public void RefreshProperty(string propName)
   {
       if (PropertyChanged != null)
           PropertyChanged(this, new PropertyChangedEventArgs(propName));
   }
}
public class ViewModel
{
   public ViewModel(SampleClass sampleClass)
   {
       WeakEventManager<SampleClass, PropertyChangedEventArgs>.AddHandler(sampleClass, "PropertyChanged", SampleClassPropertyChanged);
   }

   void SampleClassPropertyChanged(object sender, PropertyChangedEventArgs e)
   {
       // do something
   }
}

Jak widać wystarczyło wywołać tylko jedną metodę:

WeakEventManager<SampleClass, PropertyChangedEventArgs>.AddHandler(sampleClass, "PropertyChanged", SampleClassPropertyChanged);

AddHandler to statyczna metoda. Pierwszy parametr to obiekt generujący zdarzenie, drugi to nazwa generowanego zdarzenia a trzeci to handler czyli metoda obsługująca te zdarzenie. To jest na prawdę wszystko co musimy napisać! W porównaniu do poprzednich wersji WPF, kod jest dużo prostszy. Dostaliśmy generyczne rozwiązanie, które nigdy nie pozostawia tzw. sacrifice objects (patrz poprzednie wpisy).

Przejdźmy do naszego standardowego testu:

SampleClass sampleClass = new SampleClass();
ViewModel viewModel = new ViewModel(sampleClass);

sampleClass.RefreshProperty(null);
viewModel = null;
GC.Collect();
sampleClass.RefreshProperty(null);

Oczywiście SamplePropertyChanged zostanie wywołane tylko raz.  Aby przekonać się, że bez WeakEventManger mielibyśmy memory leak wystarczy podmienić wywołanie AddHandler, zwykłą silną referencją:

public class ViewModel
{
   public ViewModel(SampleClass sampleClass)
   {
       sampleClass.PropertyChanged += SampleClassPropertyChanged;
   }

   void SampleClassPropertyChanged(object sender, PropertyChangedEventArgs e)
   {
       // do something
   }
}

Po tym zabiegu SampleClassPropertyChanged zostanie wywołane dwukrotnie.