• This is a usual time of the year for summaries so let’s keep the tradition alive and write one. Here’s my 2017 achievements split between months. January Blogging for 18 days straight – nothing near gutek’s achievement (whole year!) but still a nice streak Microsoft MVP title (thx Konrad Kokosa for pointing this one ;)) February […]

  • “Advent of Code is a series of small programming puzzles for a variety of skill levels.” Each day was a fun an interesting coding challenge. I’ve decided to practice and code this in python to learn the skill. Some of them might not be the best python scripting as I was short on time in […]

  • Some time ago I’ve attended a .net developer days 2017 conference. I was quite busy since (traveling, teaching .net, working) that only now I got some time to share some thoughts about it. As a bonus I’m including a short interview I did during the event. I need to state here, just to be clear, […]

  • We developers don’t like documentation. We don’t like to read it, and we even more we don’t like to write it. But sometimes it is worth to read it. Like when you find out that by using datetime in SQL DB you got a milliseconds precision but only if it ends on 0,3,7 (link). That […]

  • In the previous post we’ve removed some of the technical debt that could be found in our NetDeveloperPoland Website application. In this one we will remove it even more. We can even maybe reach a B? Let’s see where we’ll end up at the end of this part.

W poprzednim wpisie pokazaliśmy sobie jak możemy zmusić Visual Studio do zdalnego debuggowania naszej aplikacji na produkcji. W tym wpisie przyjrzymy się MDbg. MDbg – to prosty, konsolowy debugger przeznaczony do śledzenia kodu aplikacji zarządzanych (tylko i wyłącznie – nie ma wsparcia dla aplikacji natywnych).
MDbg jest częścią instalacji Visual Studio lub Windows SDK. Działa także bez instalacji, tak więc możemy go nagrać na przenośną pamięć i nosić ze sobą w razie potrzeby. Poniżej Mdbg w działaniu – lista komend.

mdbg
Jak zacząć z nim pracę? Po uruchomieniu należy podpiąć się pod nasz proces. Wykorzystujemy do tego polecenie           (a[ttach]). Jeśli uruchomimy ją bez podania PID procesu MDbg wypisze nam wszystkie procesy zarządzane, aktualnie uruchomione. Następnie pozostaje już tylko dotrzeć do interesujących nas fragmentów aplikacji. Możemy do tego użyć takich poleceń jak: w[here] – drukuje stos wywołań, b[reak] – wyświetla/ustawia pułapki, u[p]/d[own] x – ustawia aktywną ramkę stosu (można podać o ile ma się przenieść), sh[ow] – wyświetla kod źródłowy w obrębie aktywnej instrukcji.
Zobaczymy jak z jego pomocą możemy zdiagnozować problem w aplikacji (choć w tym przypadku chyba łatwiej po prostu zerknąć na nią w Reflector’ze – tu jednak w celach pokazowych użyjemy MDbg).
Dostaliśmy informację, że nasza super inteligentna aplikacja licząca silnię działa czasami niepoprawnie. Widzimy wyniki: 5! – 120, 8! – 40320, 10! – 20922789888000. Trochę za dużo. Tak więc działamy: a 1416 i podpinamy się do naszego procesu. Za pomocą where zobaczymy stack trace i widzimy, że aplikacja czeka na wprowadzenie danych. Następnie up 5 – przechodzimy do kodu naszej aplikacji. Wtedy już tylko sh 10 – wyświetlamy 10 linijek kodu i widzimy interesujący fragment.

int num = int.Parse(input, System.Globalization.NumberStyles.HexNumber);
Console.WriteLine(Fac(num));

Widzimy więc, że nasz programista parsuje sobie liczby jako hex, dlatego wprowadzając 10 tak na prawdę liczmy 16!. Sprawdźmy… Uruchamiamy aplikację dalej (g) i wprowadzamy wartość ‘a’. Ukazuje nam się wynik 3628800 czyli poprawny.
Na koniec mała uwaga. Mdbg został napisany w kodzie zarządzanym i jeśli kogoś to interesuje to może sobie podejrzeć za pomocą Reflector’a jak wygląda kod tej aplikacji. Zachęcam.
W następnym wpisie (mam nadzieję, że tyle nie trzeba będzie na niego czekać) WinDbg.
Miłego debuggowania!

Czasami zdarzają się sytuacje, że pomimo usilnych prób nie możemy zreprodukować błędu u siebie na maszynie lokalnej. Musimy sprawdzić dlaczego nasza aplikacji źle działa na maszynie produkcyjnej. W tym wpisie postaram się przedstawić jakie narzędzia mamy do dyspozycji jeśli jeśli taka potrzeba zajdzie. Zobaczmy co jest zatem dostępne.

Visual Studio Remote Debugging

remote debuggingPierwszą naszą opcją jest Remote debugging dostępny w VisualStudio. Wraz z zainstalowanym VS instalują nam się komponenty, które możemy wykorzystać do zdalnego podłączenia się z serwerem i uruchomienia aplikacji właśnie tam; podczas, gdy pułapki i zmienne możemy sobie oglądać w naszym lokalnym VisualStudio. Jak to zrobić? Przede wszystkim na maszynie zdalnej musimy uruchomić Visual Studio Remote Debugging Monitor. Jest to małe narzędzie, które będzie monitorować przychodzące zdalne sesje debuggowania i nimi zarządzać.
vsrdm
Następnie możemy, albo uruchomić aplikację z naszego VS zdalnie albo też podpiąć się do już działającego procesu na maszynie. Zobaczmy jak skonfigurować to pierwsze. Uruchamiamy projekt naszej aplikacji i przechodzimy do właściwości. Na karcie debug wypełniamy odpowiednio. Zaznaczamy opcję Start external program i wprowadzamy ścieżkę do naszej aplikacji – taką, jaka jest na zdalnej maszynie! Następnie zaznaczamy opcję Use remote machine i wprowadzamy nazwę serwera, którą podał nam Remote Debugging Monitor. Zapisujemy ustawienia i uruchamiamy naszą aplikację. W czasie uruchamiania możemy jeszcze otrzymać komunikat o cache’owaniu symboli na zdalnej maszynie i być może będziemy musili zmodyfikować ustawienia w Option->Debugging->Symbols. Po zapoznaniu się z tą informacją nasza aplikacja jest uruchamiana na zdalnej maszynie a breakpointy, podgląd zmiennych i wszystko inne co związane z debuggowaniem widzimy w naszym lokalnym VS. Dla przykładu porównanie tego co zwraca Environment.MachineName z nazwą komputera w systemie.
p01
Druga opcja, tak jak już wspomniałem wcześniej, to podłączenie się do działającego już procesu na maszynie zdalnej. W tym celu otwieramy okno ‘Debug – Attach’ a następnie w polu Qualifier wpisujemy nazwę serwera sesji zdalnego debuggowania czyli w moim przypadku pawlos@PAWLOS-LAPTOP i po odświeżeniu listy procesów możemy podpiąć się do naszego procesu uruchomionego na zdalnej maszynie. Dalej już podobnie jak w przypadku poprzednim, działamy tak jakbyś debuggowali naszą aplikację lokalnie.

Plus? Minusy?

Jakie korzyści płyną z takiego rozwiązania? Przede wszystkim pozostajemy w VS, które większość z nas zna dość dobrze. Dzięki temu nie trzeba poznawać nowych narzędzi (choć to czasem minus), aby rozpoznać dlaczego nasza aplikacja nie działa. Plusem jest też, iż pomimo tego, że Visual Studio Remote Debugging Monitor jest instalowany razem z Visual Studio to możemy go nagrać na pendrive’a i mieć go ze sobą zawsze (ja mam na swoim zestawie niezbędnych aplikacji). Wady? Problemy z firewallem w Windows, który blokuje połączenia pomiędzy maszynami co czasem może skutecznie uniemożliwić skorzystanie z tego rozwiązania. Czy wtedy pozostajemy bez wyjścia? Nie. W następnej części zobaczymy co oferuje nam mDbg.
Miłego debuggowania.

Kilka dni temu w poście Statystyka… zapowiedziałem, iż postaram się zaproponować kawałki kodu, które umożliwią włączenie statystyk dla zapytań LINQ to SQL. Zadanie okazało się trochę trudniejsze niż myślałem, ale udało się coś osiągnąć. Zobaczmy jak.

Rozwiązanie Naïve

Pierwsze co przychodzi na myśl to proste rozszerzenie obiektu DataContext o nasze metody. Mniej więcej tak:

public static class DataContextExt
{
    public static T WithStatistics(this T context) where T: DataContext
    {
        EnableStatistics(context);
        return context;
    }
    [Conditional(“DEBUG”)]
    private static void EnableStatistics(T context) where T: DataContext
    {
        if (context != null && context.Connection as SqlConnection != null)
            (context.Connection as SqlConnection).StatisticsEnabled = true;
    }
    [Conditional(“DEBUG”)]
    public static void PrintStatistics(this T context, TextWriter writer, bool reset) where T: DataContext
    {
        if (context != null && context.Connection as SqlConnection != null)
        {
            var connection = context.Connection as SqlConnection;
            var stats = connection.RetrieveStatistics();
            Array.ForEach(stats.Cast<DictionaryEntry>().ToArray(),
                    entry => writer.WriteLine(string.Format(“Klucz: {0}, Wartość: {1}”,
                                                              entry.Key, entry.Value)));
            writer.WriteLine();
            if (reset)
                connection.ResetStatistics();
        }
    }
}

Co robimy powyżej? Rozszerzamy obiekt, który jest typu DataContext o dwie metody jak poprzednio. Pierwsza wyciąga SqlConnection i ustawia generowanie statystyk, druga drukuje raport. Nic tu nadzwyczajnego. A uruchomienie?

var dc = new DataClasses1DataContext().WithStatistics();
var query = (from t in dc.tests
            where t.t2.Contains(“12”)
            select t);
foreach (var test in query)
{
    Console.WriteLine(test.t1);
}
dc.PrintStatistics(Console.Out, false); 

Statystyki włączamy wywołując metodę .WithStatistics. Aby wydrukować statystyki posługujemy się tym samym DataContext. Jakie minusy? Brakuje nam informacji o tym jakie zapytanie wywołaliśmy – ono znajduje się w obiekcie query. Czemu zatem nie dodamy metody PrintStatistics? Niestety nie możemy tędy dostać się do obiektu SqlConnection. Przynajmniej nie bezpośrednio.

Rozwiązanie zaawansowane

Tutaj na początku wielkie podziękowania dla Marcina Najdera za podanie rozwiązania. W zasadzie 99,9% kodu poniżej to dzieło Marcina. Chcielibyśmy, aby nasz kod można było wywołać w następujący sposób:

var query = (from t in dc.tests.WithStatistics()
            where t.t2.Contains(“12”)
            select t);
foreach (var test in query)
{
    Console.WriteLine(test.t1);
}
query.PrintStatistics(Console.Out, true);
var q2 = query.Where(t => t.t2.Contains(“123”));
foreach (var test in q2)
{
    Console.WriteLine(test.t1);
}
q2.PrintStatistics(Console.Out, true);

Czyli pominięcie operowania na DataContex. Niestety, aby to osiągnąć musimy napisać własny provider LINQu. Fajnie 🙂

public static IQueryable WithStatistics(this IQueryable query, SqlConnection connection)
{
    return new StatsQueryable(query, connection.WithStatistics());
}
public static IQueryable WithStatistics(this Table table)
    where T : class
{
    return table.WithStatistics((SqlConnection)table.Context.Connection);
}

To nasze główne metody. Reszta to niestety “plumbing code”, który musimy wykonać. A zatem co robimy? Rozszerzamy obiekt Table, i zwracamy nasz nowy obiekt StatsQueryable. Zapamiętujemy sobie w nim także nasze połączenie, tak abyśmy mogli później wydrukować informacje dotyczące zapytania. Reszta kodu czyli nasz provider LINQ, który tak na prawdę robi niewiele (nic) a całą pracę deleguje do elementów, które otrzymał w konstruktorze czyli Query oraz SqlConnection.

[Conditional(“DEBUG”)]
public static void PrintStatistics(this IQueryable query, TextWriter writer, bool reset)
{
    var myQueryable = query as StatsQueryable;
    if (myQueryable != null)
        myQueryable.SqlConnection.PrintStatistics(writer, reset, query.ToString());
}
private class StatsQueryable : IQueryable
{
    protected readonly IQueryable Query;
    internal readonly SqlConnection SqlConnection;
    public StatsQueryable(IQueryable query, SqlConnection sqlConnection)
    {
        Query = query;
        SqlConnection = sqlConnection;
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return Query.GetEnumerator();
    }
    public Expression Expression
    {
        get { return Query.Expression; }
    }
    public Type ElementType
    {
        get { return Query.ElementType; }
    }
    public IQueryProvider Provider
    {
        get { return new StatsProvider(Query.Provider, SqlConnection); }
    }
    public override string ToString()
    {
        return Query.ToString();
    }
}
private class StatsQueryable : StatsQueryable, IQueryable
{
    public StatsQueryable(IQueryable query, SqlConnection connection)
        : base(query, connection)
    {
    }
    public IEnumerator GetEnumerator()
    {
        return ((IQueryable)Query).GetEnumerator();
    }
}
private class StatsProvider : IQueryProvider
{
    private readonly IQueryProvider Provider;
    private readonly SqlConnection SqlConnection;
    public StatsProvider(IQueryProvider provider, SqlConnection sqlConnection)
    {
        Provider = provider;
        SqlConnection = sqlConnection;
    }
    public IQueryable CreateQuery(Expression expression)
    {
        return new StatsQueryable(Provider.CreateQuery(expression), SqlConnection);
    }
    public IQueryable CreateQuery(Expression expression)
    {
        return new StatsQueryable(Provider.CreateQuery(expression), SqlConnection);
    }
    public object Execute(Expression expression)
    {
        return Provider.Execute(expression);
    }
    public TResult Execute(Expression expression)
    {
        return Provider.Execute(expression);
    }
}

I wszystko działa pięknie. Wynik działania:
resultLinq
Jakby ktoś potrzebował – pełny plik do pobrania: SqlStatisticsExtensions.cs
Gdyby ktoś szukał fajnych pomocy do LINQu to Marcin napisał własny provider, który umożliwia wyświetlanie naszego zapytania w rozbiciu na poszczególne części, które pozwalają nam prześledzić co dzieje się w środku – Debugging Reactive Framework (RxDebugger) and Linq to objects (LinqDebugger).
Miłego kodowania!

Tym razem będzie o statystykach bloga 🙂

visits

Nie będę pisał co mi się udało a co nie, bo w tym roku za dużo jest tych rzeczy na nie :). Zrobię tylko małe podsumowanie bloga w liczbach.

Top 3 najbardziej poczytnych postów w ciągu całego roku:
1) Visual Studio 2010 & .NET 4 – Pierwsze wrażenie. Post z ….2008 roku 🙂 – 5,368 wyświetleń
2) WPF – Podsumowanie – 2,491 wyświetleń
3) 70-502 (WPF) Przygotowania…nr 1 – 1,069 wyświetleń

PS. Tak na prawdę na drugim miejscu jest strona główna z liczbą 5,100 wyświetleń.

30 postów (strasznie mało…), 27466 wizyt oraz 49430 wyświetleń co daje 1.8 wyświetlań na wizytę.

Dziękuję wszystkim za kolejny rok wspierania bloga. Wszystkiego najlepszego w 2011 i oby był dla was jeszcze lepszy niż miniony 2010…

Nie… w tym wpisie nie będzie o statystyce odwiedzin bloga i innych podobnych rzeczach, o których można dziś na wielu blogach (u mnie o tym będzie może następny wpis :)). Będzie o klasie o której pewnie niewielu z was wcześniej wiedziało. Do wczoraj nie wiedziałem także i ja. Poznajcie – SqlStatistics. Klasa jest internal sealed tak więc czemu o niej cokolwiek piszę? A no ponieważ do samej klasy dostać się nie możemy natomiast do danych przez nią zbieranych już tak. Śledząc jej zależności Reflector’em zauważyłem, że klasa SqlConnection ma niewinną metodę RetrieveStatistics ukrytą wśród wielu innych znajdujących się tam metod.

Reflector podaje, że klasa ta zawiera takie pola jak _buffersReceived, _buffersSent, _selectCount, _selectRows i trochę innych zatem jest tam trochę danych, które mogą się nam przydać. Jak zatem możemy z tych danych skorzystać? Przede wszystkim trzeba je włączyć. Odpowiada za to właściwość EnableStatistics. Aby umożliwić jeszcze łatwiejsze z nich korzystanie postanowiłem napisać małe klasy helperów w stylu Extensions Methods.

namespace Octal.Helpers
{
    public static class SqlExtensions
    {
        public static SqlConnection WithStatistics(this SqlConnection connection)
        {
            EnableStatistics(connection);
            return connection;
        }
        [Conditional(“DEBUG”)]
        private static void EnableStatistics(SqlConnection connection)
        {
            if (connection != null)
                connection.StatisticsEnabled = true;
        }
        [Conditional(“DEBUG”)]
        public static void PrintStatistics(this SqlCommand command, TextWriter writer, bool reset)
        {
            if (command == null || command.Connection == null || writer == null)
                return;
            var stats = command.Connection.RetrieveStatistics();
            writer.WriteLine(“SQL query: {0}”, command.CommandText);
            Array.ForEach(stats.Cast<DictionaryEntry>().ToArray(),
                    entry => writer.WriteLine(string.Format(“Klucz: {0}, Wartość: {1}”,
                                                              entry.Key, entry.Value)));           
            writer.WriteLine();
            if (reset)
                command.Connection.ResetStatistics();
        }
    }
}

Co tu robimy? Definiujemy Extension Method, która ustawia właściwość EnableStatistics na połączeniu na true. Dodatkowe wywołanie metody w .WithStatistics podyktowane jest chęcią umieszczenia atrybutu Conditional. Dzięki temu nasz kod wykona się tylko w trybie Debug.
PrintStatistics to zwykłe wypisanie uzbieranych danych. Definiujemy je na klasie SqlCommand, abyśmy mogli dobrać się do treści zapytania, które wykonywaliśmy. Na koniec, w zależności od parametru resetujemy wyniki (czyli dla kolejnego zapytania będziemy mieli dane tylko tego nowego zapytania).
Jak tego używać? Bardzo prosto. Jeśli chcemy aby jakieś zapytanie było uruchomione z włączonymi statystykami musimy dodać wywołanie .WithStatistics() na połączeniu, którego dane zapytanie używa (niestety statystyki znajdują się na klasie połączenia).

using (var connection = new SqlConnection(@”Data Source=.\SQlExpress;Initial Catalog=test;Integrated Security=True”)
                                                                                                .WithStatistics())
{               
    if (connection.State != ConnectionState.Open)
        connection.Open();
    using (var command = new SqlCommand(“SELECT * FROM test t1,test t2,test t3, test t4, test t5, test t6”,
                                                                                          connection))               
    {
        using (var reader = command.ExecuteReader())
        {
            if (reader != null)
                while (reader.Read()) { }
        }
        command.PrintStatistics(Console.Out, true);
    }
    using (var cmd = new SqlCommand(“SELECT * from test”,connection))
    {
        cmd.ExecuteReader();
        cmd.PrintStatistics(Console.Out, true);
    }
}

Potem zostaje nam już tylko wydrukować nasze statystyki za pomocą PrintStatistics. Wynik?
result
Nasze dane w ładnej postaci. Oczywiście z racji atrybutu Conditional, nic z tego nie będzie działać w trybie Release. Zobaczę, czy da się to także ładnie wprowadzić do LINQu.
Miłego kodowania!

Dziś kolejne, mam nadzieję, ciekawe zastosowanie warunkowego breakpointu. Zanim jednak przejdziemy do omawiania nakreślmy naszą sytuację wyjściową.
Załóżmy, że mamy kawałek kodu aplikacji, który jest dość często wykorzystywany z różnych miejsc.

private void Calculate(params int[] coefficients)
{
    //do sth with coeffs
    Array.ForEach(coefficients, Console.WriteLine);
}

To co on robi nie jest ważne. Istotne jest, że chcielibyśmy postawić w niej breakpoint’a i zobaczyć jak się zachowuje w pewnych sytuacjach. Oczywiście chcielibyśmy, aby breakpoint nie zatrzymywał się w niej zawsze a tylko w wybranych sytuacjach. Moglibyśmy założyć warunkową pułapkę uwzględniając dane, które są w coefficients. Co jeśli one są zmienne? Wiemy jednak że wywołania, którym chcielibyśmy się przyjrzeć dokładniej pochodzą z kilku funkcji, a co więcej wiemy, że na pewno nie pochodzą z CalculateCoeffs. Spróbujmy wykorzystać warunkowy breakpoint wraz z klasami StackTrace i/lub StackFrame.

Czym jest i do czego służy StackTrace, mam nadzieję nikomu nie trzeba tłumaczyć natomiast zobaczymy co nam daje ta klasa w .NET. Pozwalają one uzyskanie ścieżki wywołań metod naszego kodu. Połączmy zatem warunkowy breakpoint ze StackTrace. Co nam wyjdzie?
conditional_stackframe
Przeanalizujmy co mamy. Tworzymy sobie ramkę stosu wywołań (można tu posłużyć się także klasą StackTrace i metodą GetFrame). Do konstruktora przekazujemy parametr 1 co oznacza, że chcemy ominąć aktualną ramkę (czyli metodę w której jesteśmy) a pobrać dane metody ją wywołującej – czyli rodzica. Dalej to już z górki. Pobieramy metodę (GetMethod) oraz jej nazwę (Name) i sprawdzamy czy nie zawiera nazwy metody, która nas nie interesuje. Voila,  gotowe.
Jako “zadanie domowe” można pokusić się o napisanie warunku, który spowoduje zatrzymanie jeśli stack trace nie zawiera wywołania z jakiegoś konkretnego modułu.
Miłego debuggowania.

Piszą swój cykl postów o WPF a w szczególności wpis, o DataBindingu pominąłem jeden ciekawy rodzaj bindingu o którym chciałbym dziś wspomnieć.
Załóżmy, że mamy UI, który wyświetla jakieś elementy, które pobierane są z WebService’u. Oczywiście strzał do WebService’u (® by Marcin Najder) jest kosztowny jeśli chodzi o czas a UI nie chcemy blokować. Możemy wykorzystać w tym celu PriorityBinding. Jak?

Załóżmy, że nasza klasa dostępu do danych wygląda następująco:

public class DataAccess
{
    public List<string> WebService
    {
        get
        {
            Thread.Sleep(5000);
            return new List<string> { “1 from WebService”, “2 from WebService”, “3 from WebService” };
        }
    }
    public List<string> Cache
    {
        get
        {
            Thread.Sleep(1500);
            return new List<string> { “1 from cache”, “2 from cache” };
        }
    }
    public List<string> Instant
    {
        get
        {
            return new List<string> { “Loading. Please wait…” };
        }
    }
}

Mamy zatem 3 źródła różniące się szybkością dostępu. Instant nie zawiera żadnych aktualnych danych, cache ma przechowywane lokalnie dane, które oczywiście mogą być nie do końca prawidłowe, natomiast WebService zwraca nam najnowsze dane. Jak napisać binding, aby nasz UI aktualizował się w miarę jak będziemy otrzymywać dane tj. aby od razu wypisał to co zwróci Instant a później gdy otrzyma dane z Cache lub z WebService zaktualizował się automatycznie? <PriorityBinding.

<ListBox>
    <ListBox.ItemsSource>
        <PriorityBinding>
            <Binding Path=”WebService” IsAsync=”True” />
            <Binding Path=”Cache” IsAsync=”True” />
            <Binding Path=”Instant” />
        PriorityBinding>
    ListBox.ItemsSource>
ListBox>

Oczywiście zakładając, że DataContext jest ustawiony na naszą klasę DataAccess. Jak to zadziała? Kolejność wpisów ustala priorytet. Im wyżej tym ważniejsze i prawdopodobnie bardziej czasochłonne wykonanie funkcji. Im niżej tym powinno ono być mniej ważne i szybsze. Dzięki ustawieniu parametru IsAsync na true, wywołania czasochłonne nie zostaną wywołane w wątku UI, blokując go jednocześnie a w wątku BackgroundWorker’a.
Efekt finalny…

Nice 🙂
Miłego kodowania!

Wszyscy wiemy, iż opanowanie operowania VisualStudio z klawiatury to podstawa do szybkiej i wydajnej pracy. Nikt nie będzie nawigował po pozycjach w menu aby dodać plik jeśli może znacznie szybciej zrobić to używając skrótu CTRL+SHIFT+A. Dziś zobaczymy jak możemy wejść na jeszcze wyższy poziom wtajemniczenia.

Zapewne wszyscy znają już ukryte funkcje FindBox’a (jeśli nie – zapoznaj się z wpisem – Ukryte możliwości FindBox’a). Dziś zobaczymy jeszcze bardziej ukryte jego możliwości. Zanim jeszcze zaczniemy musimy zacząć od podstaw podstaw. Jak szybko dostać się do tego pola? Cała zabawa traci sens, jeśli chcąc przyśpieszyć naszą pracę będziemy za każdym razem nawigować się do tego pola używając myszy.
access
Tak więc powtarzajmy jak mantrę kombinację CTRL+/ (w ustawieniach klawiatury *bodaj* General) póki nie zapadnie ona u nas na stałe w pamięci.
Dla przykładu. Znasz nazwę metody, na której chciałbyś/chciałabyś postawić pułapkę, ale nie chce ci się szukać po swoich plikach? Wpisz nazwę metody i naciśnij F9 – Bum. Masz już pułapkę na wszystkich metodach, których nazwa pasuje do tego tekstu. We wszystkich plikach (to jest i plus i minus :)).
Masz wpisany numer wiersza do którego chcesz przeskoczyć? Wystarczy wcisnąć CTRL+G i już tam się znajdziesz (choć tu szybsze jest po prostu CTRL+G i wpisanie wiersza w okienku, które się pojawi).
Zamiast chodzić po rozległych menu wpisz po prostu >NazwaMenu.NazwaPolecenia i Enter i już się ono wykona. Dla przykładu >Build.BuildSolution lub >Tools.Options. Sweet 🙂
Zaktualizowany link do opisu funkcji FindBox’a – Predefined Visual Studio Aliases

conditional_brk
Zapewne wszyscy zdają sobie sprawę, że nasz breakpoint możemy uczynić warunkowym tak aby VS zatrzymało się na nim tylko w specyficznej sytuacji a nie za każdym razem. Gdy breakpoint jest warunkowy jego ikona posiada mały biały plusik tak jak na obrazku w tym paragrafie abyśmy mogli odróżnić go od innych. Dziś pokażemy sobie, że warunkowy breakpoint może być użyty także do innych, mniej oczywistych (mam nadzieję) celów.
Załóżmy, że jesteśmy w trakcie debuggowania dość skomplikowanego kodu (ten poniżej takowym nie jest :)).

while (true)
{
    int receivedBytes = ReceivePackage();
    if (receivedBytes == Package.Length)
    {
        if (ParsePackage())
            break;
    }
}

Przetwarzamy sobie w nim jakieś pakiety. Zrobiliśmy już kawał dobrej roboty w identyfikacji i wiemy, że to ten fragment powoduje błędy. Chcemy zmusić aby flow programu przechodził jednak przez gałąź tak jakby if zwracał true. Oczywiście moglibyśmy zakończyć sesję debuggowania, zmienić kod na if (true) lub całkowicie warunek z if usunąć, tylko w jakim celu? Jeśli dość dużo czasu zajęło nam identyfikowanie problemu, lepiej nie ryzykować konieczności wykonania tej pracy raz jeszcze. Ustawmy na nim warunkowy breakpoint. Klikamy zatem na zwykłym breakpoincie PPM i wybieramy Condition…

condition_brk
Część z osób, które już wcześniej używały dobrodziejstw warunkowych pułapek, może się zdziwić i właśnie krzyczy, że jest błąd: “Tam powinien być znak ==”. Tak, zgoda. W normalnym przypadku tam powinien być znak porównania a nie podstawienia. Ale my nie chcemy normalnego przypadku a ten ciekawy :). Co się stanie w takim przypadku? Tak jak przy zwykłym wykorzystaniu tej funkcji, kod programu będzie wykonywany aż dojdzie do naszej pułapki, zostanie wykonany warunek a ponieważ nie zwróci on prawdy pułapka będzie nieaktywna. Kod wykona się dalej, ale ponieważ w naszym wyrażeniu przypisujemy do zmiennej receivedBytes wartość Package.Length warunek w if będzie prawdziwy i za każdym razem wykona się ParsePackage. Czyż to nie piękne? 🙂 Dla nieznających tej funkcjonalności dodam, że jako warunek można podać w zasadzie dowolne wyrażenie. Chcemy sobie z niego zrobić Tracepoint’a? Nie ma sprawy wystarczy napisać: Console.WriteLine(receivedBytes).I konsola zapełni nam się ładnie danymi… Na plus jest jeszcze to, że działa tam Intellisense, tak więc nie musimy wszystkiego pamiętać.
Mam nadzieję, że pokazałam dość nietypowe zastosowanie warunkowego breakpointa, które może się czasem przydać, aby oszczędzić trochę czasu podczas długich i wycieńczających sesji debuggowania :).

If debugging is the process of removing bugs, then programming must be the process of putting them in. ~Author Unknown

My software never has bugs. It works *exactly* as I wrote it! ~ Author Unknown

Dzisiejsza historia oparta jest, w większości, na faktach. Ukryliśmy tylko imię naszego programisty.

Był sobie Tomek programista. Tomek miał do napisania w C# fragment większego algorytmu. Jako, że kod był dobrze podzielony mogło to zrobić wiele osób. Jedną z rzeczy które miały być w tym fragmencie było stworzenie wektora z dwóch elementów:

[1 Math.Pow(Math.Sqrt(2), -BassCorrectRaw)-1]

Tomek nie był pozostawiony sam sobie. Miał API, które było dość rozbudowane.
Był dostępny typ VectorDouble jak poniżej:

public class VectorDouble : Vector<double>, IVectorDoubleReadOnly

{                       

    public VectorDouble() : base()

    { }

 

    public VectorDouble(int size) : base(size)

    { }

 

    public VectorDouble(int size, double value) : base(size, value) 

    { }

 

    public VectorDouble(params double[] vector) : base(vector)

    { }       

   //…

}

Wystarczyło go użyć i zainicjować wartościami co też nasz bohater uczynił. Napisał taki oto kod:

IVectorDoubleReadOnly BassCorrectCoefA = new VectorDouble(1, Math.Pow(Math.Sqrt(2), -BassCorrectRaw) – 1);

Widzicie już problem Tomcia? Jeśli chcecie pomyśleć sami to nie klikajcie na poniższy link. Jak już się zdecydujecie to zapraszam do dalszego czytania.

Pokaż rozwiązanie

Problemem Tomcia są dwa szczególne konstruktory w klasie VectorDouble. Tomek chciał wywołać konstruktor przyjmujący params double[] vector natomiast ponieważ użył wartości “1”, wykonał się nie ten konstruktor, który zamierzał. Tomek ma review kodu, ma w firmie Unit testy a jednak ten błąd przemknął się i przez dość długi czas pozostawał niewykryty powodując błędne kalkulacje. A rozwiązanie jest banalne:

IVectorDoubleReadOnly BassCorrectCoefA = new VectorDouble(1d, Math.Pow(Math.Sqrt(2), -BassCorrectRaw) – 1);

Tak. Dobrze widzicie. Poprawne i niepoprawne użycie różni się tylko literką “d”. Łatwo to przegapić.

Pytanie czy to nieznajomość Tomka spowodowała błąd czy jednak API nie jest na tyle przejrzyste, aby takim sytuacjom zapobiegać? Jak uważacie?