Portable Class Library, czyli tworzenie logiki współdzielonej cz.1

Ostatnio zauważyłem, że produkty Microsoftu skierowane dla programistów stają się coraz przyjemniejsze i coraz bardziej zautomatyzowane. Microsoft stara się jak może, żeby umilić nam codzienne zmagania w pracy. Jednym z takich produktów, o których nie wiele osób piszących aplikacje mobilne pod systemy Microsotfu (Windows Phone i Windows 8) wie jest Portable Class Library. Co to jest i co można w nim zrobić? Postaram się opisać w tym oraz w kolejnych postach dotyczących właśnie PCL-a.

Od wielu programistów Androida słyszę zarzuty, że pisanie pod Androida jest lepsze, bo robią jedną aplikację i działa ona zarówno na tabletach i smartfonach. Wtedy zazwyczaj się uśmiecham i odpowiadam, że dla mnie to największy minus Androida. :) Całe szczęście Microsoft nie poszedł w tą samą stronę, lecz pomyślał trochę i zaprezentował nam, już razem z Visual Studio 2012 bibliotekę, która może być dołączana zarówno do aplikacji Windows Phone jak i Windows Store.

pcl-createproject

Ok, trochę oszukałem… może być dołączana nie tylko do mobilnych aplikacji, ale również do aplikacji Silverlight-owych oraz zwykłych aplikacji pisanych pod .NET Framework. A nawet, za pomocą Xamarin, aplikacji Android-owych oraz IOs-owych.

PCL-Dialog

Oczywiście biblioteka działa na częściach wspólnych, więc im więcej projektów wybierzemy, tym mamy mniej możliwości do wykorzystania. Należy się więc zastanowić co chcemy umieścić jako części wspólne. Może to być oczywiście połączenie do bazy danych, nie będziemy musieli pisać 2 razy tych samych DAO, helpery działające bez specyficznych operacji dla systemu, bądź ViewModele jeśli posiadamy identyczne dla obu systemów. Dodatkowo można wydzielić interfejsy w przypadku gdy oba systemy posiadają podobną funkcjonalność, ale sama implementacja wygląda inaczej. Przykładem takiego interfejsu może być robienie zdjęć. Zarówno w Windwos 8 jak i Windows Phone 8 istnieje możliwość pobrania zdjęć z aparatu (bądź kamerki). W obu systemach jednak implementuje się to inaczej. common-architectureAktualnie właśnie zacząłem pisanie takiej biblioteki, która zawierałaby wszystkie części wspólne tych systemów i wystawiała interfejs dla PCL’a. Nie wiem ile zajmie mi napisanie implementacji 9 interfejsów, jakie do tej pory znalazłem więc nie podaje daty kiedy kod pojawi się na githubie ;)

Post ten miał na celu opisanie tego czym jest PCL i zachęcenie do używania go, nawet jeśli nie mamy w planach (na razie) robienie aplikacji na inny system. W kolejnym poście postaram się pokazać jak zaimplementować wzorzec MVVM używając PCL i MVVMLight.

Pierwsze zabawy z MongoDb

Już od dłuższego czas chciałem poznać jedną z baz NoSql-owych. Niestety zawsze jak się zabierałem za poznawanie jakiejś miałem nad głową termin skończenia projektu i wolałem zrobić coś szybko w znanych mi już relacyjnych bazach danych. Teraz zacząłem projekt, którego zakończenie przewiduje na marzec, więc mam jeszcze trochę czasu żeby pobawić się czymś nowy. Jedną z nowości, które zamierzam lepiej poznać to darmowa, NoSql-owa baza danych MongoDB. Jest to baza dokumentowa, więc wszystkie dane przechowuje w postaci plików json. Podejście to projektowania danych jest trochę inne niż w bazach takich jak MS SQL Server, ponieważ nie ma tutaj relacji. Oczywiście można sobie zamodelować przechowywanie kluczy, ale lepiej jest trzymać wszystko w jednym dokumencie.

Czego potrzebujemy żeby zacząć przygodę z MongoDB?

Przede wszystkim musimy pobrać bazę ze storny producenta (http://www.mongodb.org/downloads). Następnie rozpakowujemy pobrane archiwum i właściwie już możemy odpalić bazę. Aby to zrobić uruchamiamy konsole (cmd/powershell), wchodzimy do katalogu bin i piszemy:

.mongod.exe -dbpath sciezka_do_katalogu_z_danymi

Nie zamykamy konsoli, gdyż spowoduje to zamknięcie bazy. Domyślnie nasza baza zostanie uruchomiona na 127.0.0.1:27017. Niestety nie możemy uruchomić żadnego panelu admina ani nic co pomoże nam zarządzać bazą poprzez przeglądarkę. Do tych celów polecam program Robomongo. Dzięki niemu będziemy mogli podejrzeć dane, dodać, aktualizować czy wywoływać własne skrypty.

 

Czas połączyć to z .NET-em

MongoDB świetnie integruje się z .NET-em. Cała dokumentacja jak zrobić bardziej zaawansowane rzeczy znajdziecie na stronie http://docs.mongodb.org/ecosystem/drivers/csharp/ Przede wszystkim warto przeczytać CSharp Driver LINQ Tutorial, żeby zobaczyć jak używać LINQ do wyciągania danych z bazy.

Do używania mongo potrzebne są dwie biblioteki: MongoDB.Bson.dll oraz MongoDB.Driver.dll, obie można zainstalować poprzez Package Manager z lini poleceń:

PM> Install-Package mongocsharpdriver

lub poprzez wizualny manager NuGet’a (Official MongoDB C# Driver). Tyle wystarczy żeby móc już używać mongoDB w swoim projekcie.

Jako bonus dołączam moją implementację wzorca Repository skierowanego specjalnie dla MongoDB. Klasa ta posiada, kilka moich zdaniem kluczowych funkcjonalności takich jak:

  • zwraca nie cała listę obiektów, a obiekt IQueryable, który pozwala na tworzenie kwerend LINQ,
  • zwraca obiekt po jego Id,
  • przy dodawaniu nowego obiektu generuje mu nowe Id,
  • jeśli nie podamy nazwy kolekcji stworzy automatycznie kolekcje odpowiadającą nazwie typu obiektu, który zapisujemy w bazie.
/// <summary>
/// This class provides Id property
/// </summary>
public abstract class ModelBase
{
    public string Id { get; set; }
}

public class MongoRepository<T> 
    where T : ModelBase
{
    public MongoDatabase Database { get; private set; }

    private string _tableName;

    public MongoRepository(string tableName)
    {
        this._tableName = tableName;
        CreateDatabase();
    }

    public MongoRepository()
    {
        CreateDatabase();
    }

    private void CreateDatabase()
    {
        MongoClient client = new MongoClient();
        MongoServer server = client.GetServer();
        Database = server.GetDatabase("databasename");
    }

    public IQueryable<T> GetAll()
    {
        var col = GetTColection();
        return col.AsQueryable<T>();
    }

    public T GetById(string id)
    {
        var col = GetTColection();
        return col.AsQueryable<T>().Single(e => e.Id == id);
    }

    public void Add(T obj)
    {
        if (obj.Id == null )
        {
            obj.Id = ObjectId.GenerateNewId().ToString();
        }
        var col = GetTColection();
        col.Insert(obj);
    }

    public void Remove(string id)
    {
        var col = GetTColection();
        col.Remove(Query.EQ("_id", id));
    }

    public void Update(T obj)
    {
        var col = GetTColection();
        col.Save(obj);
    }

    private MongoCollection<T> GetTColection()
    {
        if (!string.IsNullOrEmpty(_tableName))
            return Database.GetCollection<T>(_tableName);
        else
            return Database.GetCollection<T>(typeof(T).Name);
    }
}

Łatwiejsza praca z bazą danych Azure Mobile Services

Ostatnio zacząłem bawić się z Windows Azure Mobile Services, ponieważ chciałem zobaczyć czy, to rozwiązanie będzie wystarczające do aplikacji, którą teraz piszę. Nie spodobał mi się jednak sposób jaki ekipa Microsoftu sugeruje wyciąganie danych w aplikacji, czyli przechowywanie uchwytu do bazy Mobile Service jako statycznej właściwości w klasie App. Wiele przyjemniejszym rozwiązaniem jest stworzenie sobie klasy Repozytorium, która będzie pośrednikiem między aplikacją a serwisem wyciągającym dane. Najprostsze repozytorium może wyglądać tak:

public class ServiceRepository<T>
{

    public static MobileServiceClient MobileService = new MobileServiceClient(
        "https://linktoservice.azure-mobile.net/",
        "SECRETPASSWORD");

    public async Task<T> Add(T obj)
    {
        await MobileService.GetTable<T>().InsertAsync(obj);
        return obj;
    }

    public async Task Delete(T obj)
    {
        await MobileService.GetTable<T>().DeleteAsync(obj);
    }

    public async Task<T> Update(T obj)
    {
        await MobileService.GetTable<T>().UpdateAsync(obj);
        return obj;
    }

    public IMobileServiceTable<T> Get()
    {
        return MobileService.GetTable<T>();
    }
}

Ponieważ każdy element w bazie WAMS posiada swoje Id, możemy trochę rozszerzyć działanie repozytorium wprowadzając interfejs IServiceModel, po którym będą dziedziczyć wszystkie inne modele.

interface IServiceModel
{
    int Id { get; set; }
}

Dzięki temu, możemy dodać do repozytorium jeszcze jedną bardzo przydatną metodę:

public class ServiceRepository<T>
    where T : IServiceModel
{

    //...

    public async Task Get(int id)
    {
        var obj = await MobileService.GetTable()
            .Where(e => e.Id == id)
            .Take(1)
            .ToListAsync();

        if (obj != null && obj.Any())
            return obj.First();

        throw new InvalidIdException();
    }
}

Stworzenie takiej klasy daje nam dużo bardziej czytelny kod i pozwala nam zmniejszyć liczbę zmian w systemie, jeśli w przyszłości okazałoby się, że jednak nie będziemy korzystać z WAMS, a czegoś innego.

Lazy Loading kontekstu aplikacji

W większościach aplikacji, kontekst ładujemy na starcie. Wtedy mamy najwięcej czasu na zużycie jak największych zasobów procesora przez większą ilość czasu. System ten sprawdza się w grach, zdarzają się jednak aplikacje, które nie zawsze korzystają z załadowanego kontekstu. Warto wtedy zastosować, coś co nazywa się “leniwym ładowaniem”. Oznacza to, że odpowiednie zasoby ładujemy dopiero w chwili kiedy się do nich odwołujemy po raz pierwszy. Dajmy na to, że mamy obiekt ApplicationContext, który zawiera wiele elementów, które aplikacja przy zamykaniu zapisuje w pamięci, natomiast przy uruchamiania wczytuje je z pamięci. Obiekt ten zawiera również jakiś obiekt BigObject, do którego to elementu odwołujemy się tylko w specjalnych sytuacjach, dlatego nie chcemy tego obiektu ładować przy starcie.

public class ApplicationContext
{
    public BigObject BigObject { get; private set; }

    public void LoadContext() {
        BigObject = LoadObject();
    }

    private BigObject LoadObject() {
        //loading object from memory
    }
}

Wystarczy mała zmiana kodu aby stworzyć obiekt, który ładuje się dopiero po odwołaniu się do niego. Ciekawe jest to, jak niewiele kodu trzeba napisać, żeby osiągnąć pożądany efekt.

public class ApplicationContext
{
    private BigObject _bigObject;
    public BigObject BigObject {
        get { return _bigObject ?? (_bigObject = LoadObject()); }
    }

    public void LoadContext() {
    }

    private BigObject LoadObject() {

    }
}

Często jednak wczytanie kontekstu wiąże się z odczytywaniem czegoś z pamięci, więc z operacjami, które mogą trwać sporo czasu jeśli obiekt do załadowania jest sporych rozmiarów. Dzięki temu, że w C# można tak łatwo tworzyć funkcje asynchroniczne, po małej modyfikacji (wystarczy opakować zmienne w klasę Task) możemy stworzyć obiekt, który wczytywać się będzie asynchronicznie.

public class ApplicationContext
{
    private Task<BigObject> _bigObject;
    public Task<BigObject> BigObject {
        get { return _bigObject ?? (_bigObject = LoadObject()); }
    }

    public void LoadContext() {
    }

    private Task<BigObject> LoadObject() {
        //loading object from memory
    }
}

W tym przypadku należy pamiętać, aby przed odwołaniem się do właściwości BigObject postawić słówko kluczowe await.

var bo = await appContext.BigObject;

Jednak czekać na wczytanie danych będziemy tylko za pierwszym razem, każde kolejne odwołanie się do BigObject zwróci nam jego wartość automatycznie.

TypeScript, czyli pomoc w okiełznaniu JavaScript

Jak większość programistów, która wychowała się na tworzeniu aplikacji okienkowych nie jestem wielkim fanem JavaScirpt’u. Niemniej jednak język ten zyskuje coraz większą popularność, nie tylko wśród Web Developerów. Nie można zignorować tego jak bardzo największe firmy (takie jak Microsoft) zachęcają do tworzenia aplikacji mobilnych za pomocą HTML i JavaScript, dlatego myślę, że w niedługim czasie o ile już nie są, programiści tego języka będą bardzo cenni.

In JavaScript, arrays are objects, functions are objects, regular expressions are objects, and, of course, objects are objects.

Sam JavaScript jest językiem dla mnie językiem dziwnym. Można w nim zrobić rzeczy, o jakich nie śniło się nawet zwykłym programistom języków C – podobnych. Niestety zbyt duże możliwości jakie daje ten język nakładają się na to, że o wiele ciężej wykryć błąd w programie, bo tak na prawdę kompilator nie wie czy to błąd, czy użyliśmy tego specjalnie.

Programiści języków statycznie typowanych przyzwyczajeni są, że kompilator im krzyknie kiedy będą chcieli przesłać stringa, zamiast inta. Stety/niestety w JavaScripcie nie było takich możliwości. Ostatnio jednak coraz większą popularność zyskują biblioteki pozwalające na okiełznanie trochę samego JavaScript. Jedną z tych bibliotek, jest rozwijana przez Microsoft, TypeScript.

TypeScirpt

TypeScript w żaden sposób nie zastępuje kodu JS, jest bardziej rozszerzeniem jego składni. Edytor TypeScript wyświetla na raz zarówno kod TS jak i JS, dzięki czemu wiemy, czy na pewno chodzi nam o to co piszemy. Na stronie typescriptlang.org jest dostępnadokumentacja, z której możemy dowiedzieć się jakie funkcjonalności posiada biblioteka. W niniejszym poście postaram się przedstawić tylko największe zalety tego rozszerzenia.

Sprawdzanie typów

selekcja
JavaScript nie jest językiem silnie typowanym, więc zmienna, która jest napisem, za chwilę może być liczbą, lub funkcją. Tak samo zmienne przesyłane do funkcji nie mają w żaden sposób sprawdzanych typów. Tak więc jeśli chcemy być 100% pewni, że przesłana zmienna jest odpowiedniego typu musimy sprawdzać za każdym razem typy argumentów. Na szczęście TypeScript wprowadza możliwość określenia jakiego typu argumentu oczekujemy w funkcji. Nie pozwala to jednak korzystać z przesilania funkcji, bo koniec końców i tak cały kod jest konwertowany na JS.

function add(a: number, b : number){
   return a + b;
}
var sum = add(2, 3);    //OK
sum = add(2.1, 2.3e3);  //OK
sum = add(2, "Ola");    //ERR

Jest to najprostszy przykład sprawdzania argumentów przesłanych do funkcji, Warto jednak zauważyć, że błąd, który wyskoczy przy ostatnim wywołaniu funkcji, wyskakuje jedynie w edytorze TypeScript, więc jeśli ktoś chciałby użyć tej funkcji z poziomu zwykłego JavaScript operacja byłaby w pełni dozwolona, co więcej funkcja zwróciłaby wynik: “2Ola”.

Sprawdzanie typów nie ogranicza się tylko do wartości przychodzących do funkcji, można również określić jakiego typu zmienną, funkcja zwróci. Jeśli tego nie zadeklarujemy, kompilator sam sprawdzi jaki typ zwracamy i zmienną zadeklarowaną z wartości zwróconej będzie traktować jako właśnie takiego typu.

function add(a: number, b : number) : string{
   //return a + b;             //ERR
   return (a + b).toString();  //OK
}

Ostatnią rzeczą, odnośnie sprawdzania typów, którą zamierzam opisać to sprawdzanie jaką funkcję przesyłamy jako argument. Jak było wspomniane na początku w JS nawet funkcje są obiektami, więc często funkcje przesyła się jako parametr innej funkcji. Dzięki określeniu interfejsu funkcji możemy zadeklarować, że oczekujemy funkcji, która przyjmuje liczbę i zwraca liczbę. Jest to coś na kształt delegatów w języku C#.

function doSomething (arg : string, foo : (a : number) => number){
   return arg + " : " + foo(2).toString();
}
var foo2 = function (x : number){
   return "-" + x;
}
/*
Supplied parameters do not match any signature of call target: Call signatures of types
'(x: number) => string' and '(a: number) => number' are incompatible
*/
var res = doSomething("Jan", foo2);     //ERR

Klasy

u have no class
JavaScript jest językiem, w którym klasy określamy poprzez prototyp. Każdy obiekt, posiada swój prototyp, do którego możemy dodawać funkcje, przez co zyskujemy coś na kształt klas. Dziedzicznie natomiast jest tak zawiłe, że trzeba się sporo napisać, żeby osiągnąć wymagany efekt. Na szczęście TypeScript pomoże nam również z tym. Dzięki dobrze znanej z języków C-podobnych składni można definiować klasy, prywatne bądź publiczne zmienne, oraz metody. Poniższy przykład posiada: prywatną zmienną, konstruktor, publiczną metodę oraz prywatną metodę. Podobnie jak we wcześniejszym przypadkach, prywatne zmienne oraz metody są tylko umowne w kodzie TypeScript. Więc z poziomu kodu JavaScript można zarówno się odwołać do zmiennej name jak i wywołać metodę setName.

class MyClass {
   private name : string;

   constructor (nam : string){
      this.setName(nam);
   }
   getName() {
      return this.name;
   }
   private setName(value){
      this.name = value;
   }
}
var ob = new MyClass("Ala");
document.writeln(ob.getName());  
//Alaob.setName("Ola");  //ERR
ob.name = "Ola";    //ERR

Dziedziczenie

Dziedziczenie obiektów jest banalnie proste i oszczędza pisania bardzo wielu linijek kodu. Przykład ze strony typescriptlang.org świetnie pokazuje wszystkie zalety dziedziczenia.

class Animal {
   constructor(public name: string) { }
   move(meters: number) {
      alert(this.name + " moved " + meters + "m.");
   }
}
class Snake extends Animal {
   constructor(name: string) { super(name); }
   move() {
      alert("Slithering...");
      super.move(5);
   }
}
class Horse extends Animal {
   constructor(name: string) { super(name); }
   move() {
      alert("Galloping...");
      super.move(45);
   }
}

var sam = new Snake("Sammy the Python");
var tom: Animal = new Horse("Tommy the Palomino");
sam.move();
tom.move(34);

W obu przypadkach zostaną wywołane funkcje przeciążające.

Intefejsy

Implementowanie interfejsów, chociaż podobne jest do dziedziczenia po klasach, to używane jest do trochę innych rzeczy. Dlatego twórcy TypeScript również pozwolili na tworzenie interfejsów w JavaScripcie. W interfejsach możemy deklarować zmienne bądź metody publiczne. Klasy implementujące te interfejsy muszą zagwarantować obsługę wszystkich metod oraz stworzenie zmiennych przedstawionych w interfejsie. Implementacja odbywa się za pomocą słówka implements, czyli po Javovemu.

interface SayHello {
   hello ();
}
class Animal {
   name : string;
   constructor (name : string){
      this.name = name;
   }
}
class Snake extends Animal implements SayHello{
   hello(){
      return ("ssss " + this.name + " ssss");
   }
}
class Duck extends Animal implements SayHello {
   hello (){
      return ("quack " + this.name + " quack");
   }
}

function echo (soundSource : SayHello){
   alert(soundSource.hello());
   alert(soundSource.hello());
}

var tom = new Snake ("Tom");
var james = new Duck("James");
echo(tom);
echo(james);

Dodatkowe linki
Aby móc korzystać z TypeScript w Visual Studio, trzeba najpierw pobrać plugin. Polecam również zainstalować dodatek do Visual Studio – Web Essentials 2012, który pozwoli na kompilacje kodu TS na JS z poziomu Visual Studio. Poza tym świetny edytor do nauki TypeScript można znaleźć na stronie domowej TypeScript.

Windows 8 Extensions

Już jakiś czas temu udostępniłem na github’ie projekt o nazwie Win8Extensions, w którym miałem umieszczać klasy, które mogą się przydać podczas pisania aplikacji na Windows 8. Na razie aplikacje Windwos Store, które stworzyłem można liczyć na palcach jednej ręki, natomiast z doświadczenia wiem, jakie klasy są zawsze potrzebne podczas pisania tego typu aplikacji. Stworzyłem ten projekt również po to aby zobaczyć co inni myślą o moim kodzie. Jeśli ktoś zajrzy w kod z chęcią wysłucham co powinienem poprawić… Oczywiście komentarze staram się pisać, ale ciężko mi to idzie ;)

W projekcie tym możecie znaleźć przede wszystkim różnego rodzaju konwertery, które nie są tworzone tworzeniu nowego projektu Windows Store, a które mogą się przydać. Między innymi ObjectToVisibilityConverter, który sprawdza czy obiekt jest pusty (null) i jeśli tak zwraca Visibility.Collapsed. W ten sposób możemy ukryć na ekranie ramki, które mają wyświetlić obiekt, ale tylko jeśli nie jest on pusty.

Inną ciekawą klasą, która może zainteresować programistów Windows Store Apps jest ApplicationDataSerializer, który za pomocą jednej funkcji zapisuje i odczytuje wybrany obiekt z i do podręcznej pamięci programu. Obecnie korzysta on z seriazliacji do json’a, natomiast łatwo go przerobić tak aby serializował do xml’a.

Z biegiem czasu, jeśli jeszcze będę pisał aplikacje na Windows 8 będę starał się dodawać nowe rzeczy do biblioteki, może jakieś nowe kontrolki… już nawet mam kilka pomysłów co to mogłoby być. Niczego jednak nie obiecuje, bo jest to jednak projekt robiony w wolnym czasie, więc może być różnie.

Tak jak pisałem wcześniej, jest to moja pierwsza biblioteka OpenSource, więc jeśli ktoś będzie miał jakieś opinie lub pomysły co można tam jeszcze uwzględnić, niech do mnie napisze. No i oczywiście zachęcam do korzystania z tych klas w swoich własnych projektach.

Win8Extensions na GitHub

Singleton w trzech linijkach

Mówi się, że singleton to najprostszy i jeden z najprzydatniejszych wzorców projektowych. Coś w tym jest, przydaje się zwłaszcza w aplikacjach mobilnych, gdzie nie zawsze możemy przesyłać do innej strony cały jej content. Przydaje się wtedy obiekt, który współdzieli content stron. Możemy to również uzyskać poprzez statyczny obiekt w klasie, natomiast dzięki stworzeniu singletonu nie musimy robić wszystkich metod i pól statycznych, a używanie go później nie różni się niczym od używania zwykłego obiektu. Zastosowanie tego wzorca posiada również inne plusy, ale chciałbym się skupić bardziej na jego implementacji.

Pisząc go po raz n-ty, z małą pomocą ReSharpera udało mi się go streścić do 3 linijek, a wygląda on następująco:

class MySingleton
{
 private static MySingleton _instance;
 public static MySingleton Instance { get { return _instance ?? (_instance = new MySingleton()); } }
 private MySingleton() { }
}

Pobranie instancji singletona wygląda tak:

var mySingleton = MySingleton.Instance;

Jeśli chodzi o inne wzorce projektowe to polecam książkę “Wzorce projektowe”, Bandy Czworga. Podstawową literaturę dla każdego programisty.

Podejście do asynchroniczności w .NET

Każdy ostatnio zachwyca się największą nowością jaka przyszła z .NET Framework 4.5, czyli słówkach kluczowych async i await. W poniższym poście postaram się przedstawić do czego służą i czy na prawdę tak bardzo ułatwiają pracę z metodami asynchronicznymi.

Na początku chciałbym przyjrzeć się na dwóch wzorcach pracy z asynchronicznością, które istniały wcześniej. Mowa tu o Asynchronous Programming Model (APM) oraz Event-based Asynchronous Pattern (EAP)

Asynchronous Programming Model

Zwany również jako Begin/End pattern. Polegał na tworzeniu powiązanych ze sobą metod BeginMehtodName oraz EndMethodName. Przy czym BeginMethodName rozpoczynała metodę asynchorniczną oraz zwracała obiekt implementujący interfejs IAsyncResult natomiast EndMethodName czekała aż metoda się skończy. Co nam to dawało? Przede wszystkim możliwość wykonania innych operacji między wywołaniem Begin a End. Trzeba było jednak samemu się domyślić ile operacji możemy zrobić, żeby po wywołaniu End nie czekać znów zbyt długo. W metodzie Begin mogliśmy też podać delegata AsyncCallback, który wywoływał się kiedy operacja się skończyła.

Przykładem takiego modelu może być odczyt danych ze strumienia.

var buff = new byte[1024];
var asyncResult = stream.BeginRead(buff, 0, 1024, ReadComplete, null);

//..do some other stuff

stream.EndRead(asyncResult);

Event-based Asynchronous Pattern

Model oparty na zdarzeniach był już łatwiejszy do zaimplementowania. Generował natomiast bardzo wiele kodu. Model ten polega na tym, że dla każdej metody: MethodNameAsync tworzony był delegat MethodNameCompletedEventHandler, przyjmujący obiekt typu MethodNameEventArgs oraz tworzone było zdarzenie MethodNameCompleted.

class MyClass
{
   public void ReadAsync(byte[] buffer, int offset, int count) {
      //.. do some asynchronously
      // then call ReadCompleted
   }
   public event ReadCompletedEventHandler ReadCompleted;
}

public delegate void ReadCompletedEventHandler(object sender, ReadEventArgs eventArgs);

public class ReadEventArgs : AsyncCompletedEventArgs
{
   // class body
}

Dodatkowo, jeśli chcieliśmy aby metoda, którą przypisujemy do zdarzenia MethodNameCompleted robiła coś na wątku UI, musieliśmy tworzyć Dispatchera, aby jej to umożliwić.

Task-based Asynchronous Pattern

Tak więc jak działa model oparty na Taskach? Powyższy przykład moglibyśmy zapisać tak:

public class MyClass
{
   public Task ReadAsync(byte[] buffer, int offset, int count)
   {
      //...
   }
}

Różnić się będzie jedynie wywołanie takiej metody. Ponieważ metoda ReadAsync zwraca obiekt typu Task, możemy więc użyć przy niej słówka await, które działa mniej więcej tak:

Załóżmy, że mamy taką klasę AsyncClass z metodą GetRandomNumberAsync, która to metoda losuje jakąś liczbę całkowitą czeka wylosowaną ilość sekund, po czym zwraca tą liczbę.

internal class AsyncClass
{
   public Task GetRandomNumberAsync()
   {
      return Task.Run(() =>
      {
         var rand = new Random();
         var randNum = rand.Next(20);
         Thread.Sleep(TimeSpan.FromSeconds(randNum));
         return randNum;
      });
   }
}

Wywołanie tej metody wygląda następująco:

static async void Foo()
{
   var asyncObject = new AsyncClass();
   var num = await asyncObject.GetRandomNumberAsync(); //(1)
   Console.WriteLine(num); //(2)
}

W linii (1) metoda używa słówka await, więc kompilator automatycznie przerzuca to co wykonuje się po tej linijce do oddzielnej metody, która wywoła się dopiero kiedy metoda GetRandomNumberAsync zwróci jakiś wynik. Dodatkowo to co wywoła się w lini (2) zostanie wywołane w wątku UI, więc możemy się z tego poziomu odwoływać do elementów okna, bez potrzeby uruchamiania Dispatchera.

Jak widać, wszystkie powyższe podejścia do asynchroniczności są dostępne i dalej używane w .NET Framework 4.5. Ostatnie podejście skierowane jest specjalnie do aplikacji graficznych, w których metody przeważnie powodują odświeżenie okna wyników. Dzięki async i await możemy pozbyć się bardzo dużej ilości niepotrzebnego kodu.

Mam nadzieję, że już wiesz, czym różnią się te trzy podejścia i jak z nich korzystać. Jeśli chciałbyś dowiedzieć się więcej o asynchroniczności w .NET polecam bardzo lekturę dokumentu Task-based Asynchronous Pattern, stworzonego przez Microsoft, specjalnie aby wytłumaczyć nowe podejście do asynchroniczności.

INotifyPropertyChanged bez wpisywania nazwy właściwości.

Każdy, kto choć przez chwilę miał przyjemność pisać jakąś aplikacje w .NET używającą bindowania zapewne zna doskonale ten interfejs. Sam spędziłem mnóstwo czasu pisząc w setterach do właściwości OnPropertyChanged(„NazwaWłaściwości”);

Możecie otworzyć szampana, gdyż w .NET Framework 4.5 możemy się pozbyć bezmyślnego kopiowania nazwy właściwości.

Jak było wcześniej?

Wcześniej wywołując event PropertyChanged dla właściwości trzeba było podać jej nazwę.

class SimpleClass : INotifyPropertyChanged
{
    private int _myProperty;
    public int MyProperty {
       get { return _myProperty; }
       set
       {
          _myProperty = value;
          OnPropertyChanged("MyProperty");
       }
    }
 
    public event PropertyChangedEventHandler PropertyChanged;
 
    private void OnPropertyChanged(string propertyName)
    {
       if (PropertyChanged != null)
       {
          PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
       }
    }
}

Jak wiadomo, nie służyło to refactoringowi. Wystarczyło zmienić nazwę właściwości a cały program się sypał i widok nie był odświeżany, gdy powinien.

Jak jest teraz?

Tak jak jednak wspominałem z pomocą przychodzi nam atrybut CallerMemberName, który jak możemy się domyślić zwraca nam nazwę właściwości/metody, która wywołała metodę.
Jak go użyć? Dodajemy ten atrybut przed deklaracją parametru do funkcji OnPropertyChanged oraz ustawiamy parametrowi wartość domyślną (null). Dzięki temu możemy wywołać funkcję OnPropertyChanged bez żadnych parametrów, a kompilator sam będzie wiedział co go wywołało.

class SimpleClass : INotifyPropertyChanged
{
   private int _myProperty;
   public int MyProperty {
      get { return _myProperty; }
      set
      {
         _myProperty = value;
         OnPropertyChanged();
      }
   }
 
   public event PropertyChangedEventHandler PropertyChanged;
 
   private void OnPropertyChanged([CallerMemberName]string propertyName = null)
   {
      if (PropertyChanged != null)
      {
         PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
      }
   }
}

O programowaniu słów kilka

Z okazji przeprowadzania właśnie refactoringu jednego z projektów postanowiłem napisać kilka rzeczy, które gdy widzę sprawiają, że odechciewa mi się zmiany czegokolwiek.

1. Im mniej komentarzy tym lepiej.

Tak, wyznaję zasadę, która ma tyle samo zwolenników co przeciwników. Nie mniej jednak komentarz w kodzie występuje przeważnie tam, gdzie coś nie działa tak jak chciał programista i w ten sposób chce przestrzec inne osoby przed problemem, który on napotkał. Pomijam tu komentarze opisujące klasy i metody, jeśli są one wymagane do przyszłej dokumentacji, to jak najbardziej powinny być pisane, ale za pomocą ctrl + m + ctrl + o, można je bardzo prosto schować i nie są one nikomu potrzebne. Jednak jeśli kod jest dobrze napisany, czyta się go jak dobrą książkę. Jeśli czegoś szukamy patrzymy jak nazywa się funkcja i co po kolei robi. Te dwie rzeczy powinny nam starczyć, żeby zorientować się czy to czego szukamy jest gdzieś tutaj, czy może w innej części kodu.
Pozostają jeszcze przyzwyczajenia z kodu C++, czyli komentarze-szlaczki wyglądające mniej więcej tak:

///////////////////////////////////////////////////////////////////////////////////////////////////////
public void Refresh_Click(object sender, EventArgs e){
}
///////////////////////////////////////////////////////////////////////////////////////////////////////

Wiem, bo sam takie pisałem kiedy zaczynałem swoją przygodę. Jest to fajne, jeśli piszemy w notatniku i nie możemy zwinąć funkcji. Kiedy jednak mamy tak wspaniałe narzędzie jak visual studio nie musimy się bawić w oddzielanie poszczególnych funkcji, bo w tym momencie szlaczki są ważniejsze od tego co się dzieje w klasie.

2. Dziel klasy na foldery

Tak się dzieje, że kiedy zaniedbamy na samym początku podział klas na foldery, to później bardzo ciężko jest to zrobić. Im większy staje się projekt tym gorzej się połapać w tym gdzie może znajdować się dana klasa. O ile sami siedzimy w tym od początku, to programista, który dostanie nasz kod może mieć spore problemy z odnalezieniem właściwego pliku. Bardzo podoba mi się sposób w jaki programuje się w Javie, czyli podział kodu na paczki. Tam tworzymy paczki i dzielimy kod na oddzielne regiony. W C# moim zdaniem zrobione jest to trochę gorzej. Programista widząc foldery myśli, że w folderach jak to w folderach, powinien umieszczać obrazki, ikony i inne zasoby. Nie martwi się o podział klas na foldery. Tak podział daje porządek w solucji i projektach, chociaż i tak uważam że podział na paczki w Javie jest dużo fajniejszy.

3. Nie komentuj, wyrzuć

Zasada ta wydaje się szczególnie uzasadniona, kiedy korzystamy z systemu zarządzania wersjami. Często sam się łapie na tym, że komentuje kod, bo może kiedyś będę musiał do tego wrócić. Po zmianie jednak zapominamy o tych wszystkich zakomentowanych rzeczach i zostają one w projekcie. Sam widząc taki blok, jeśli nie jest on mój długo waham się czy go wywalić, czy zostawić. Przeważnie jednak wywalam. Jeśli ktoś chce wrócić do starej wersji, może przejrzeć starszego commita, a na 90% jeśli już ten blok zakomentował i zcommitował to nie wróci do starej wersji.

4. Nie bój się dodawać klas.

Zauważyłem, że wiele osób ma problemy z dodawaniem nowej klasy. Na początku oczywiście tworzymy klasy bo są nam potrzebne, ale wraz z tym jak projekt się rozrasta coraz mniej klas jest dodawanych. Wolimy dodawać nową metodę i nowe argumenty niż stworzyć nową klasę. Dochodzi czasami nawet do takich paradoksów, ze skoro mamy obiekt który przechowuje jakieś dane to w tym samym obiekcie przechowywaliśmy listę tych obiektów?! Wydaje mi się jednak, że w programowaniu obiektowym dodanie nowej funkcjonalności powinno oznaczać dodanie nowej klasy, a nie dodanie nowej metody. Tak się robiło pisząc programy strukturalne, pora się przenieść na obiekty…

5. Używaj regionów

Jedna z tych rzeczy, które sprawiają, że pisanie w C# staje się dużo wygodniejsze niż w Javie to właśnie regiony.
Tak wygląda klasa, która w rzeczywistości ma 400 lini:

6. Interfejs to fajna sprawa

Może trochę za często nawiązuje do Javy, wiem, że niektórzy ludzie nawet nie chcą słyszeć, że istnieje coś takiego, ale moim zdaniem Java ma też kilka bardzo dużych zalet i każdy programista .NET powinien poznać chociaż powierzchownie tą technologie. Jedna z rzeczy, nad którymi się często zastanawiam, to czemu w C# nie piszę interfejsów, albo jeśli piszę to wygodniej mi jest jeśli bym ich nie pisał. Nie umiem tego wytłumaczyć, wiem, że to czy używamy interfejsów, to sprawa architektury systemu. Więc technologię odsuwamy na bok, niemniej jednak pisząc w Javie miałem taki wewnętrzny przymus pisania interfejsów, natomiast pisząc w C# olewam to i piszę oddzielne klasy. Zauważyłem, że interfejsów używa niewielka ilość osób piszących w C#, natomiast znaczna większość osób piszących w Javie. Nie mam pojęcia czemu, ale interfejsy to bardzo fajna sprawa i warto ułożyć architekturę aplikacji, tak żeby jednak ich używała, ułatwi to bardzo rozszerzanie funkcjonalności.

7. Nie kopiuj, zrób funkcję

Ktoś, kiedyś, chyba na studiach powiedział mi, że jeśli programista pisze drugi raz ten sam kawałek kodu powinien się zastanowić, czy nie stworzyć nowej funkcji, a jeśli pisze go trzeci raz to już na pewno powinien ją stworzyć. Wprawdzie dziwi mnie to, że ktoś woli pisać 4 razy te same 10 linijek niż skrócić to do jednej funkcji i wywoływać ją w różnych miejscach, przecież informatycy to leniwi ludzie. Powinno nam zależeć an tym, żeby pisać jak najmniej…

Wpis ten poświęcam Panu z żółtą stroną internetową 🙂