Month: January 2016

Zakładki w Visual STudio

Posted on

Bookmark – zakładka – stosowana aby zaznaczyć miejsce (linię) w kodzie. Np aktualnie pracujemy nad czymś w kilku miejscach na raz i chcemy szybko przechodzić między tymi miejscami.

Są różne zakładki. Dotychczas korzystałem z CTRL+k+K do ustawiania i CTRL+k+n do przechodzenia do następnej zakładki.

Inną (potencjalnie lepszą) opcją jest ustawianie zakładek numerowanych. Poprzez CTRL+SHIFT+1 ustawiamy zakładkę o numerze 1 a poprzez CTRL+1 przechodzimy do tej zakładki. I tak dalej dla kolejnych numerków.

visual studio bookmark

Timeout czyli kolejny plus dla DateTime.UtcNow

Posted on Updated on

Taki “synchronizator”, który dodatkowo przerwie czekanie po kilku sekundach.

private static void WaitWithTimeout()
{
    var startTime = DateTime.Now;

    while (true)
    {
        lock (SyncRoot)
        {
            if (!_isOperationInProgress)
            {
                return;
            }
        }

        if (DateTime.Now - startTime > TimeSpan.FromSeconds(3))
        {
            throw new TimeoutException("Max duration exceeded");
        }

        Debug.WriteLine("Waiting for xxx with timeout...");
        Task.Delay(TimeSpan.FromMilliseconds(50)).Wait();
    }
}

Użycie DateTime.Now sprawia, że ten kod wywali się na pewno raz w roku podczas zmiany czasu na letni/zimowy czy tam na odwrót. Oczywiście optymiści powiedzą że na pewno nam się to nie przytrafi bo prawdopodobieństwo małe… Ach Ci optymiści 😀 A potem grzebiesz w gównie (najczęściej nie swoim) bo nie możesz zreprodukować.

DateTime.UtcNow zrobi robotę.

private static void WaitWithTimeout()
{
    var startTime = DateTime.UtcNow;

    while (true)
    {
        lock (SyncRoot)
        {
            if (!_isOperationInProgress)
            {
                return;
            }
        }

        if (DateTime.UtcNow - startTime > TimeSpan.FromSeconds(3))
        {
            throw new TimeoutException("Max duration exceeded");
        }

        Debug.WriteLine("Waiting for xxx with timeout...");
        Task.Delay(TimeSpan.FromMilliseconds(50)).Wait();
    }
}

Kiedy nazwane argumenty mogą pomóc

Posted on Updated on

Taki kod:

var users = await UploadUsers();

Po zrozumieniu jak działa await/async (np po artykule Async and Await: Context) wiem, że powinno się dodawać metodę ConfigureAwait()it():

var users = await UploadUsers().ConfigureAwait();

Podczas Code Review zauważyłem, że kolega wrzucił jednak taki kod:

var users = await UploadUsers().ConfigureAwait(continueOnCapturedContext: false);

Nie rozumiałem po co dorzucać nazwane argumenty z wartością taka jak domyślna. Przekowałem się że jest to jednak czytelniejsze “co tak naprawdę się dzieje”, ponieważ samo ConfigureAwait() nie wiele mówi. Ostatecznie skończyło się na extension method:

    public static class TaskExtension
    {
        public static ConfiguredTaskAwaitable ConfigureAwaitWithoutReturnToContext(this Task task)
        {
            return task.ConfigureAwait(continueOnCapturedContext: false);
        }
    }

Czyszczenie w repo

Posted on Updated on

Wyrzucenie wszystkich plików pozostałych po rozwiązywaniu konfliktów (*.orig):
Z konsoli unixowej:

find Mobile -type f -name '*.orig' | xargs rm

Na kiedyś

Napisać programik, który z katalogu i podkatalogów usuwa wszystkie pliki *.cs, które nie wchodzą w skład żadnego *.csproj. Są to pliki pozostałości po złych mergach (bo jak zostawię za dużo to nikt mi głowy nie urwie…).

Wielowątkowość w teorii i praktyce

Image Posted on Updated on

multithreading in theory and practice

Korzystanie z async/await to ciężki kawałek chleba więc polecam lekturę Don’t Block on Async Code (w pigułce fajne rzeczy).

Coś o estymowaniu

Posted on

estymujemy na 2 punkty

“to zadanie jest banalne, wyestymujmy je na 2pkt”

Pisanie kodu w Xamarin Studio (na Macu) zamiast VS

Posted on Updated on

Thinks to keep in mind when using Xamarin Studio on Mac:
– Change formatting to VS: Preferences->Source Code->Code Formatting->C# source code->Policy->MS Visual Studio
– Do not indent new lambda brackets (indent blocks inside expressions) Preferences->Text Editor->Behavior->Indentation->Indentation mode->Automatic

Turn of asking about converting End Of Lines:
Tools->Options->General->Line ending conversion->Leave line ending as is

Xamarin: uruchamianie kodu UI, własny Dispatcher

Posted on Updated on

Xaramin Forms sprawia wiele problemów i chciałem opisać jeden z nich. To wszystko kiedyś oczywiście może zacząć działać lepiej, niemniej obecnie (styczeń 2016) są problemy.

Aplikacja z którą pracuję jest obecna dwóch platformach Android oraz iOS.

UiThread, UiContext, wątek UI, – nieistotne nazwa, chodzi o to samo.

Zakładam, że czytelnik wie dlaczego zmiany na UI trzeba wykonywać z wątku z którego ten UI był stworzony. Nie będę też tłumaczył dlaczego długotrwałe operacje nie robimy na wątku UI.

Wspólna metoda Device.BeginInvokeOnMainThread() i kłopoty

Jest wspólna dla obydwu platform metoda Device.BeginInvokeOnMainThread(Action action). Ta metoda powoduje zakolejkowanie akcji do wykonania na UiThread. Metoda zaczyna się od Begin więc nie jest tutaj nic blokowane, zaraz skaczemy do następnej linijki.

Device.BeginInvokeOnMainThread(() =>
{
    IsErrorVisible = false;
});
Foo(); // will be executed right after above line, probably before above action is executed

Z takim kodem na Androidzie są 2 problemy.
– nie jest nawet sprawdzone czy jesteśmy w UiThread, w związku z tym nawet gdy jesteśmy i akcja mogłaby być wykonana synchronicznie od razu to i tak jest wrzucana do kolejki.
– z testów wyszło że czasem akcja rozpoczyna wykonanie 1s później, bez jakichkolwiek wyraźnych przesłanek dlaczego tak późno. Nie wiadomo dlaczego tak jest… (ok, chodzi o jakiś stary mechanizm synchronizacji ze starego .NETa, który jest wykorzystywany przez Mono na Androidzie – generalnie ten mechanizm jest słaby)

Na iOS te problemy nie występują/są mniejsze. Niby fajnie, ale teraz jest problem bo kod wspólny (proste wywołania na UI) wygląda inaczej gdy jest wykonany na różnych urządzeniach.

Rozwiązaniem jest własny Dispatcher

który opakowauje wywołania BeginInvokeOnMainThread i traktuje je inaczej per platforma.

public interface IDispatcher
{
    void ToUi(Action action);
}

Implementacja na Androida

public class AndroidDispatcher : IDispatcher
{
    public void ToUi(Action action)
    {
        if (Looper.MainLooper.Thread == Thread.CurrentThread())
        {
            action();
            return;
        }

        var activity = Forms.Context as Activity;
        if (activity != null)
        {
            activity.RunOnUiThread(action);
        }
    }
}

Najpierw pierwszy if:
Jeśli jesteśmy w wątku UI to po prostu wykonaj synchronicznie akcje. Nie trzeba się bawić w asynchroniczności po prostu wykonujemy co mamy zrobić.

if (Looper.MainLooper.Thread == Thread.CurrentThread())
{
    action();
    return;
}

W drugiej części kodu jest to, co powinno być od razu (w Device.BeginInvokeOnMainThread()) robione dobrze.

var activity = Forms.Context as Activity;
if (activity != null)
{
    activity.RunOnUiThread(action);
}

RunOnUiThread() zgodnie z dokumentacją, wykonuje akcje od razu gdy w UiThread lub wrzuca do kolejki oczekujacej na UiThread. Implementacja natywna po prostu zachowuje się lepiej.

Runs the specified action on the UI thread. If the current thread is the UI thread, then the action is executed immediately. If the current thread is not the UI thread, the action is posted to the event queue of the UI thread.

Implementacja na iOS

Podobnie jak wcześniej sprawdzamy czy jesteśmy w UiThread i wykonujemy akcję od razu. Jeśli nie jesteśmy to korzystamy z natywnej wersji.

public class IosDispatcher : NSObject, IDispatcher
{
    public void ToUi(Action action)
    {
        if (NSThread.Current.IsMainThread)
        {
            action();
        }
        else
        {
            InvokeOnMainThread(action);
        }
    }
}

Implementacja na iOS (Xamarin.Forms)

Wersja Forms ma więcej problemów i tutaj musimy ostatecznie użyć Device.BeginInvokeOnMainThread(). Jego implementacja na iOS jest zdecydowanie lepsza niż na Androida więc odpalenie akcji będzie w miarę szybkie (bez zbędnych opóźnień).

public class IosDispatcher : IDispatcher
{
    public void ToUi(Action action)
    {
        if (NSThread.Current.IsMainThread)
        {
            action();
        }
        else
        {
            Device.BeginInvokeOnMainThread(action);
        }
    }
}

Cała rozkminka dzięki Waldkowi Zubikowi.

Wiecej loguj

Posted on Updated on

Napisz o co chodzi

Debug.WriteLine(uri.ToString());

A można:

Debug.WriteLine(string.Format("Requested uri is '{0}'", uri));

A jeśli wiemy że uri jest trochę tajemnicze i chcemy mieć więcej informacji to:

Debug.WriteLine(string.Format(
    "Requested uri is '{0}'. Requesting product number '{1}' from marketplace '{2}'", 
    uri, 
    productNumber, 
    marketplaceName));

Dodaj więcej informacji

if (synchronizationId.HasValue == false)
{
    throw new AppException("Cannot start syncing products.");
}

Może iterujesz po kolekcji produktów i warto byłoby coś o nich napisać.

if (synchronizationId.HasValue == false)
{
    throw new AppException(string.Format(
        "Cannot start syncing products. Product.Name: '{0}', Product.Version: '{1}'", 
        productInfo.Name,
        productInfo.Version));
}

W jaki sposób się to zapisze jest mniej istotny – to tylko przykład.

Dodaj więcej informacji #2

if (modifyRowAction == null || modifyCellAction == null)
{
    throw new InvalidOperationException();
}

Patrząc w logi, bez odtwarzania całego scenariusza (i brake’owania) nie jesteśmy w stanie powiedzieć który warunek sprawił, że został rzucony wyjątek.

Nie ukrywaj wyjątków

catch (Exception)
{
    // Do something other than rethrow
}

Lepiej

catch (Exception ex)
{
    _logger.WarnEx(ex, "happened when...");
    // Do something other than rethrow
}

Dobra planszówka na początek

Posted on Updated on

Moze zaczniemy od tego jakiej nie kupić, ponieważ usłyszałem niezależnie od kilku osób chcących zacząć z planszówkami, że rozpoczną od tej gry:

Nie kupuj Monopoly!

Czy też polskiej wersji “Eurobiznes”. Ta gra ma kilka wad które mogą skutecznie zniechęcić.
– jest baaardzo losowa – to naprawdę frustrujące jeśli masz cały czas pecha i dostajesz po dupie.
– akcja (myślenie) dzieje się tylko dla jednego gracza gdy wykonuje on swój ruch. W innych grasz podczas ruchu przeciwnika możesz dalej obmyślać jak odpowiesz na jego ruchy. Tutaj wszystko zależy od losowego rzutu kostką i dopiero wtedy sytuacja może się diametralnie zmienić.
– jeśli na początku gdy poszło Ci źle to jest duża szansa że będziesz się do końca gry po prostu nudził – słabe jak na grę gdzie wszyscy powinni się dobrze bawić.
– tylko jeden cel – kasa, kasa, kasa. Są gry gdzie punkty zdobywa się na różne sposoby

Eurobiznes – tak, ja też mam sentyment do tej gry. Raz nawet zrobiłem ją sobię całą od nowa z pamięci (bo nie znałem innych sposobów na jej posiadanie), oczywiście ceny były na “oko” więc grywalność też była na oko;) Sentyment sentymentem, ale naprawdę są lepsze gry i lepiej się z tą nie męczyć.

Filmik (Game Troll TV – 10 Najbardziej Przereklamowanych Gier Planszowych według Widzów!) na którym (oprócz własnych doświadczeń) mogę podepszeć opisane wady tej gry:

Jeszcze muszę tylko dopisać w której minucie gadają o Monopoly

Monopoly w obronę

Jeden z komentarzy pod filmem na youtube:

Nie przeginajcie z tym Monopolem! To jest przede wszystkim klasyk i tak trzeba go rozpatrywać. Na tle współczesnych gier wypada bardzo blado, to prawda, ale to gra z lat 30ych. Ja osobiście ją pamiętam jako Eurobusiness z lat 80ych i w tych latach była naprawdę świetną planszówką. Może w dzieciństwie mieliście dostęp do lepszych gier, ale dla mnie i myślę że dla wielu z mojego rocznika, była to wtedy jedna najlepszych gier.

Inne fajne gry (już godne polecenia)

* Osadnicy z katanu,
* Carcassone,
* Agricola

Gier planszowych jest mnóstwo, każdemu przypadnie coś innego do gustu.