Witaj na Zine.net online Zaloguj się | Rejestracja | Pomoc

Visual Studio 2010 Community Launch we Wrocławiu

Zapraszamy na



Visual Studio 2010 Community Launch to cykl konferencji, które odbędą się w największych miastach Polski podczas których to zostaną zaprezentowane nowości z świeżo wydanego Visual Studio w wersji 2010 oraz najnowszego .NET Framework 4.

Już 24 kwietnia (sobota) w Instytucie Informatyki UWr przy ul. Juliot-Curie 15 odbędzie się pierwsze spotkanie z tego cyklu. Podczas 5 sesji tematycznych zostanie zaprezentowany bardzo szeroki wachlarz nowości, od ogólnego pokazania 'co nowego' poprzez najnowsze zmiany w języku C# a skończywszy na programowaniu równoleglym i IntelliTrace'ie.

Więcej informacji o wydarzeniu jak i agendę można znaleźć na stronie: http://ms-groups.pl/vs2010cl/Strony/VS2010CL_Wroclaw.aspx
Wśród uczestników spotkania podczas konferencji zostaną rozlosowane nagrody.

Wydarzenie jest bezpłatne lecz wymagana jest wcześniejsza rejestracja. Liczba miejsc jest ograniczona.

Serdecznie zapraszamy do udziału,
Wrocławska Grupa .NET
opublikowano przez nuwanda | 6 komentarzy

Zintegruj się z NDepend

Dość niedawno na swoim blogu Patrick Smacchia ogłosił, że NDepend jest teraz w 100% zintegrowany z Visual Studio. Nie ma co ukrywać, że jak mamy coś pod ręką to zdecydowanie łatwiej po dane narzędzie sięgnąć i zastosować. Testowałem NDependa już kilkakrotnie i za każdym razem dawał świetne wyniki. Zapytacie do czego mi się przydał?

Nie będę się tu rozpisywał o funkcjach tego narzędzia. Było kilku przede mną, którzy to już uczynili: Darek, Gutek i Paweł. Skupię się na zastosowaniach.

Po pierwsze jest nieocenionym źródłem informacji na temat metryk dotyczących naszego kodu. W kilka sekund możemy dowiedzieć się, które metody / klasy nadają się pod lupę.

Jednak prawdziwa moc drzemie w CQLu. Możliwość odpytywania kodu analizowanej aplikacji jak bazy danych pozwala w mgnieniu oka wyciągnąć informacje, które w inny sposób musielibyśmy agregować zbyt długo, aby nadal nas interesowały. Miałem taki przypadek, w którym chodziło o znalezienie wszystkich typów (serwisów biznesowych), które dziedziczyły po pewnej specyficznej klasie bazowej. Aplikacja była duża i wśród 150 klas znalazło się 30, które szybko zlokalizowałem i poprawiłem. Wyszukiwanie (z pisaniem zapytania CQL) trwało jakieś 10 sekund :).

Drugą fajną sprawą jest analiza zależności. W dużych projektach często nie jesteśmy w stanie ogarnąć wszystkich modułów i zależności między nimi. W projekcie na którym testowałem NDependa jest około 10 modułów (każdy po kilka assembly) i pracuje nad nim 40 programistów. Mając pewne założenia co do podziału na warstwy i zależności między tymi warstwami ciężko jest wymuszać te ustalenia ręcznie. Po szybkiej analizie zależności okazało się, że mamy kilka niechcianych powiązań, które udało nam się wyeliminować. Co więcej zapytania CQL można zapisać w postaci WARN IF [zapytanie] i zaszyć w kodzie. NDepend można wpiąć w proces ciągłej integracji i tak spreparowane zapytania walidujące NDepend potrafi odczytywać i informować o naruszeniach. Dzięki temu będziemy mieli natychmiastową informację, gdy pojawi się niechciana zależność.

Ostatnio pracowałem też nad tematem wersjonowania API systemu. Chcąc utrzymać kompatybilność wstecz dążymy do tego, aby żadna do tej pory istniejąca klasa/element klasy nagle nie zniknął w kolejnej wersji. NDepend pozwala porównywać dwie wersje aplikacji i budując odpowiednie zapytania CQL możemy walidować API. Podobnie jak w przypadku wyżej opisanej analizie zależności dostaniemy informację, gdy coś nam z API zniknie.

W podobny sposób możemy badać pokrycie naszego kodu przez testy jednostkowe. NDepend potrafi czytać dane z NCovera i Visual Studio Team System.

Jednak co najważniejsze w tym poście to fakt, że nowa wersja NDependa, jest w pełni zintegrowana z Visual Studio 2005, 2008 i 2010. Zainstalowałem ją na VS2010 RC i jestem pod wrażeniem użyteczności. To co zrobił team Patricka jest świetne, gdyż najbardziej irytował mnie fakt, że aby przeanalizować jakiś projekt musiałem odpalić zewnętrzne narzędzie, wczytać plik i operować gdzieś na boku. Co więcej czasami UI NDependa pozostawiało mały niesmak, gdyż nie zawsze chciało współpracować. Teraz to się zmieniło na lepsze. Uruchomienie analizy w projekcie, który jeszcze nie był analizowany jest proste i sprowadza się do wyboru z menu NDependa komendy „Attach new NDepend project to current solution”. Potem po krótkiej analizie wszystko dzieje się w Visual Studio w jego oknach narzędziowych. Mniam!

Historia lubi się powtarzać

Nieco ponad dwa lata temu pisałem o grupach pasjonackich we Francji w odniesieniu do tego co działo się w Polsce. Był koniec roku 2007, a ja chwilowo mieszkałem  poza granicami naszego kraju. W tym właśnie czasie powstawało wiele, dziś prężnie działających, grup ludzi spotykających się by dzielić się wiedzą. Te wspaniałe inicjatywy napawały mnie radością i chęcią do działania.

Jedną z takich grup była ta zlokalizowana we Wrocławiu, która zaczęła się organizować właśnie pod koniec 2007 roku i ruszyła z końcem listopada. Studiowałem we Wrocławiu i z żalem opuszczałem to miasto, widząc na horyzoncie powstającą grupę. Dlatego też z takim zainteresowaniem obserwowałem działania polskich grup z zagranicy. Pisząc „jeszcze tam wrócę”, nie wiedziałem jak i czy w ogóle wrócę kiedykolwiek do Wrocławia.

Moja droga powrotna do Polski zaprowadziła mnie do Łodzi. Jak się szybko okazało, Łódź jako jedno z największych miast nie miało swojej zorganizowanej społeczności technologii Microsoft. Na mapce społecznościowej na środku Polski była wielka dziura. Na szczęście z Łodzi do Warszawy jest rzut beretem, a na miejscu koledzy i koleżanki z Warszawskiej Grupy .Net są bardzo gościnni.

Jednak długo nie musiałem tak jeździć, gdyż okazało się, że oprócz mnie na MTS 2008 znalazło się jeszcze czterech łodziaków, którzy z taką samą pasją włączyli się w organizację Łódzkiej Grupy Profesjonalistów IT & .Net. Grupa ruszyła w grudniu 2008 i działa prężnie do tej pory :). Myślę, że sukcesem organizacji tej grupy było rozproszenie odpowiedzialności. Tworząc ją w pięć osób mieliśmy 5 razy więcej pomysłów, 5 razy więcej siły, 5 razy więcej zaangażowania i 5 razy więcej czasu. Dzięki temu wiele tematów mogło być dogranych bardzo dobrze. Dzięki chłopaki za tamten rok!

Po roku spędzonym w Łodzi, nagle otworzyła się furtka i okazało się, że jednak wrócę do Wrocławia. Na jedenastym spotkaniu pożegnałem się z grupą (nawet dostałem tort :) i od sierpnia mieszkam nad Odrą w krainie krasnali.

Jak się szybko okazało wróciłem do Wrocławia za późno. Po grupie, której narodziny obserwowałem i do której bardzo chciałem dołączyć, nie zostało już prawie śladu. Postanowiłem wtedy, że coś z tym faktem muszę zrobić. Mieszkając we Wrocławiu zawsze uważałem, że to miasto ma wielki potencjał jeżeli chodzi o technologie informatyczne i na dodatek było sygnatariuszem porozumienia „Autostrada Firm Nowych Technologii”, czyli inicjatywy utworzenia „Polskiej Doliny Krzemowej”. Wrocław potrzebuje miejsca, gdzie pasjonaci technologii Microsoft będą mogli się spotkać, poznać i wymienić doświadczenia.

Skończył się rok 2009. Jak zwykle w tym okresie robimy pewne postanowienia na rozpoczynający się rok. Jednym z moich postanowień noworocznych jest ponowne zaangażowanie się w działania społecznościowe. Nie wiedziałem jeszcze jak i czy, ale czułem, że długo nie będę musiał czekać. Odkąd znów zacząłem pracować we Wrocławiu rozmawiałem często z Marcinem Najderem, kolegą z firmy, o konieczności reaktywacji grupy wrocławskiej. I tuż po Nowym Roku Marcin skontaktował mnie z Pawłem Łukasikiem, aktywnym działaczem Krakowskiej Grupy Developerów .Net i założycielem dotnetomaniak.pl, który również chciał reaktywować grupę. W ciągu tego dnia nasze skrzynki mailowe huczały, gdyż wymieniliśmy między sobą ponad 50 maili. Wszystko zaczęło się kręcić w zawrotnym tempie. Chwilę później dołączył do nas Mirosław Pragłowski. To utwierdziło mnie w przekonaniu, że historia zaczyna się powtarzać :).

Wcześniejsze doświadczenia naszej czwórki w organizowaniu działań społecznościowych zaowocowały reaktywacją grupy w niecały miesiąc. Od pierwszego maila 10 stycznia do ogłoszenia reaktywacji grupy 3 lutego nie minął nawet miesiąc. Jestem pod wrażeniem tych działań i zaangażowania.

Wrocławska Grupa .Net oficjalnie zaczęła swoje życie w Sieci. 16 lutego oficjalnie rozpocznie działanie pierwszym spotkaniem reaktywujacym (dwudziestym licząc od początku). W momencie pisania tego tekstu liczba zarejestrowanych uczestników tego spotkania wynosi 47 (w tym dwie osoby towarzyszące), a to dopiero czwarty dzień od ogłoszenia terminu. Bardzo cieszy mnie takie zainteresowanie bo to właśnie ludzie są najważniejsi w społeczności.

Wszystkich tych, którzy nie słyszeli jeszcze o reaktywacji, albo słyszeli, ale jeszcze się nie zarejestrowali, zapraszam do uczestnictwa w spotkaniu. Zachęcam Was do rejestracji na stronie spotkania, gdyż mając statystyki uczestników spotkań grupy będziemy mogli pochwalić się nimi przed potencjalnymi sponsorami i lepiej przygotowywać kolejne spotkania.

Jestem pewien, że razem zbudujemy we Wrocławiu społeczność technologiczną, której tak tu brakuje. Już niedługo będziesz miał(a) okazję znaleźć się w gronie pasjonatów takich jak Ty. Nie przegap tej okazji! Spotkaj się z nami we wtorek, 16 lutego w Instytucie Informatyki Uniwersytetu Wrocławskiego na spotkaniu reaktywacyjnym. Następnie spotykaj się z nami regularnie w każdy trzeci wtorek miesiąca. Jest to niepowtarzalna okazja by poznać profesjonalistów pracujących w Twojej branży, na co dzień spotykających się z problemami takimi jak Ty.

Przychodź, poznawaj, dziel się i rozwijaj!

opublikowano przez nuwanda | 7 komentarzy

VS2010 – block selection

Na blogu, który dotyczy samego edytora w Visual Studio natknąłem się na ciekawy wpis o zaznaczaniu bloków, którego co prawda nie ma w becie pierwszej wydanej niedawno, ale podobno ma się znaleźć w wersji finalnej. Zapowiada się obiecująco.

opublikowano przez nuwanda | 21 komentarzy
Filed under:

Na CodeCampie byłem, sesję zrobiłem.

ccwawa09_400x140

Dzisiejszy dzień spędziłem na konferencji Code Camp w Warszawie. Po raz pierwszy byłem w roli prelegenta. Poprzeczka została ustawiona bardzo wysoko już na pierwszej sesji, którą prowadził Tomasz Kopacz.

Ja miałem przyjemność poprowadzić trzecią z kolei sesję. Zaprezentowałem publiczności projekt, który od jakiegoś czasu rozwijam. Jest to wtyczka do ReSharpera, o której pisałem w poprzedniej notce. Miałem w końcu okazję powiedzieć nieco więcej o samej idei wyjątków kontrolowanych, którą za pośrednictwem przygotowanej wtyczki wprowadzam do języka C#. Było trochę o historii, trochę o problemach rozwiązań już istniejących. Z sesji jestem zadowolony. Ocenę pozostawiam uczestnikom.

Tuż po moim wystąpieniu na blogu Mariusza Koprowskiego, mojego kolegi z Łódzkiej Grupy Profesjonalistów IT & .Net, pojawiła się kompozycja zdjęć wykonana w technologii Photosynth. Można ją obejrzeć tu.

Wielkie podziękowania należą się organizatorom. Konferencja była w moim odczuciu bardzo udana.

Dla zainteresowanych jeszcze raz podaję stronę projektu: http://exceptionalplugin.codeplex.com/

W załączniku do notki znajdują się też slajdy.

Wyjątkowe wydanie (exceptional release)

Jak można było się domyślić moje zainteresowanie ReSharperem oraz dodatkami do niego nie wzięło się znikąd. Jakiś czas temu wpadła mi do głowy myśl, aby napisać dodatek, który pomagałby mi w pisaniu niezawodnego kodu.

Na co dzień pracuję przy dużym projekcie, składającym się z wielu modułów. Pisząc własny moduł często zmuszony jestem korzystać z API innych modułów. Wszystko jest pięknie, dopóki nie zaczynają się pojawiać tzw. unhandled exceptions. W Javie mamy checked exceptions. Dzieki nim mamy pewność, ze żadnego wyjątku nie przegapiliśmy. Stwierdziłem, że R# będzie dobrą podstawą do zaimplementowania takiego mechanizmu dla C#. Co więcej pozwoli zaimplementować także rozwiązania pojawiających się problemów.

W moim zamyśle do deklaracji rzucanych wyjątków służy dokumentacja xml, która dostarcza znacznika <exception />. W nim możemy udokumentować pojedynczy wyjątek rzucany z dokumentowanego elementu. Zadaniem dodatku jest przeanalizowanie wyjątków wyrzucanych z tymi udokumentowanymi. Na tym algorytmie bazuje pierwsza wersja, którą właśnie udostępniłem.

Zapraszam na stronę projektu, gdzie znajdują się szczegóły.

http://exceptionalplugin.codeplex.com/

opublikowano przez nuwanda | 23 komentarzy

Wtyczki do ReSharper 4.5 – Odc. 4 – R#4.5 Beta i errata do trzeciego odcinka

Dokładnie tego samego dnia, w którym opublikowałem trzecią część tej serii ukazała się oficjalna wersja beta ReSharpera 4.5. Z jednej strony ucieszyłem się, że w końcu będę mógł zobaczyć kolejne wydanie (nocnych buildów nie instalowałem z racji ich małej stabilności). Z drugiej jednak strony trochę się zmartwiłem, bo prawdopodobnie odcinek trzeci właśnie mocno stracił na aktualności. Na blogu ReSharper Horizons przeczytałem, że

„… we decided that performance, memory usage and building a foundation for further improvements were of a higher priority than API compatibility. Without that massive code cleanup we would have been spending more and more resources to make our product better for you, and without much success. Please accept our apologies, plug-in developers!”.

Nie będę na zaistniałą sytuację narzekał, bo zgadzam się z ich założeniami. Ciekawiło mnie jednak jak bardzo moja wiedza na temat wewnętrznej struktury R# się przedawniła.

Odnosząc się do poprzedniego odcinka zmian na szczęście nie będzie wiele. Zacznijmy tak jak poprzednio od implementacji Deamon Stage.

[DaemonStage]
public class SharpedDaemonStage : CSharpDaemonStageBase
{
public override IDaemonStageProcess CreateProcess(IDaemonProcess process, DaemonProcessKind processKind)
{
if (process == null) return null;
if (IsSupported(process.ProjectFile) == false) return null;

return new SharpedDaemonStageProcess(process);
}

public override ErrorStripeRequest NeedsErrorStripe(IProjectFile projectFile)
{
return ErrorStripeRequest.STRIPE_AND_ERRORS;
}
}

Jedyna zmiana, jaką można zauważyć, to drugi parametr metody CreateProcess. Z tego co wyczytałem w krótkim opisie zmian parametr ten może posłużyć np. do wyłączenia naszego etapu z procesu analizy całego solution, ale w większości przypadków można go zignorować.

Drugim elementem był Daemon Stage Process.

internal class SharpedDaemonStageProcess : CSharpDaemonStageProcessBase
{
public SharpedDaemonStageProcess(IDaemonProcess daemonProcess)
: base(daemonProcess)
{
}

public override void Execute(System.Action<DaemonStageResult> commiter)
{
HighlightInFile(file => file.ProcessDescendants(this), commiter);
}

public override void VisitThrowStatement(IThrowStatement throwStatementParam)
{
this.AddHighlighting(new ThrowHighlighting(throwStatementParam));
}
}

Tu też zmiany są niewielkie. Trochę zmieniło się wywoływanie procesu analizy drzewa pliku. Samo wywołanie metody ProcessDescendants zostało takie samo. Na podstawie tego, co powiedział mi wuj Reflector to widać refaktoryzację procesu podświetlania. Teraz nie dostajemy bezpośrednio pliku, na którym mamy działać, ale zostało to ukryte w metodzie HighlightInFile, która wywołuje naszego delegata z odpowiednim plikiem.

protected void HighlightInFile(Action<ICSharpFile> fileHighlighter, Action<DaemonStageResult> commiter)
{
fileHighlighter(this.File);
commiter(new DaemonStageResult(this.myHighlightingInfos));
}

Ostatnim elementem była klasa podświetlenia. Tam też nie dostrzeżemy wielu zmian.

[StaticSeverityHighlighting(Severity.WARNING)]
public class ThrowHighlighting : CSharpHighlightingBase, IHighlighting
{
private IThrowStatement ThrowStatement { get; set; }

public ThrowHighlighting(IThrowStatement throwStatement)
{
ThrowStatement = throwStatement;
}

public override bool IsValid()
{
return true;
}

public override DocumentRange Range
{
get { return this.ThrowStatement.ToTreeNode().ThrowKeyword.GetDocumentRange(); }
}

public string ErrorStripeToolTip
{
get { return "This is throw statement! (on a strip)"; }
}

public int NavigationOffsetPatch
{
get { return 0; }
}

public string ToolTip
{
get { return "This is throw statement! (tool tip)"; }
}
}

Jedyną różnicą jest fakt, że zostaliśmy zobligowani do zaimplementowania jeszcze jednej metody – IsValid. Z tego, co się orientuję to za pomocą wyniku działania tej metody możemy powiedzieć R#owi, że nasze podświetlenie jest z jakichś powodów nieaktualne, niepoprawne.

Jak widać zmian w naszym przykładzie nie było zbyt wiele, ale można się było tego spodziewać, jako że nie wykorzystujemy jakoś bardzo najważniejszych elementów R#. To dopiero przed nami. Z bezpośrednio zauważalnych zmian widać, ze liczba zestawów (ang. assembly) chyba się podwoiła. Poza tym rzeczywiście przerobili część podstawowych elementów systemu, ale teraz nie ma sensu wchodzić w szczegóły. JetBrains udostępniło migration guide, którym pokrótce omówili najważniejsze zmiany.

  1. R#4.5 Beta i errata do trzeciego odcinka (ten tekst)
  2. ...
opublikowano przez nuwanda | 5 komentarzy
Filed under: ,

Attachment(s): SharpedPlugin_4.zip

Wtyczki do ReSharper 4.x – Odc. 3 – Analiza kodu i podświetlanie

zine.net.pl.resharper.plugins.03.deamon.running

Jednym z głównych zadań ReSharpera jest analiza kodu i dostarczanie sugestii oraz rozwiązań dla znalezionych problemów. Otwierając plik z kodem naszego programu widzimy jak R# go analizuje - widoczna jest taka strzałka na pasku (ang. stripe) jak po lewej. W tym momencie działa w tle osobny wątek, który nazywa się ‘Daemon’. To w tym wątku wykonywane są wszystkie analizy. Proces analizowania pliku składa się z etapów (ang. stage). R# pozwala na dołączanie własnych etapów do tego procesu i w tym odcinku pokażę właśnie jak to zrobić. 

Zadanie jest proste i sprowadza się do zaimplementowania dwóch klas – Daemon Stage oraz Daemon Stage Process. Pierwsza z nich jest odpowiedzialna za utworzenie drugiej, mówiąc wprost pierwsza jest punktem wejścia, dzięki któremu R# będzie mógł dołączyć nasz etap do całej analizy. Zacznijmy od implementacji Daemon Stage.

   1: [DaemonStage]
   2: public class SharpedDaemonStage : CSharpDaemonStageBase
   3: {
   4:     public override IDaemonStageProcess CreateProcess(IDaemonProcess process)
   5:     {
   6:         if (process == null) return null;
   7:         if (IsSupported(process.ProjectFile) == false) return null;
   8:  
   9:         return new SharpedDaemonStageProcess(process);
  10:     }
  11:  
  12:     public override ErrorStripeRequest NeedsErrorStripe(IProjectFile projectFile)
  13:     {
  14:         return ErrorStripeRequest.STRIPE_AND_ERRORS;
  15:     }
  16: }

Poprawna implementacja Daemon Stage wymaga dwóch elementów. Po pierwsze zaimplementować należy interfejs IDaemonStage oraz klasę oznaczyć atrybutem DaemonStageAttribute. W powyższym przykładzie poszliśmy trochę dalej i wykorzystaliśmy klasę bazową CSharpDaemonStageBase, która dostarcza nam metodę pomocniczą IsSupported, sprawdzającą czy analizowany plik jest plikiem C# (ciekawskich odsyłam do Reflectora). Tak na marginesie to bez Reflectora praca nad jakąkolwiek wtyczką byłaby niemożliwa i jest to narzędzie, z którego korzystam przez 80% pisania wtyczki. Wracając do powyższej implementacji jasnym jest już chyba, że zadaniem metody CreateProcess jest dostarczenie instancji naszego procesu, który będzie analizował plik. Zaimplementujemy go za chwilę.

Zatrzymajmy się jeszcze przy drugiej metodzie – NeedsErrorStripe. Jej zadaniem jest określenie podstawowych właściwości procesu. Do wyboru mamy trzy wartości: None, Stripe oraz Stripe_and_Errors. Pierwsza z nich oznacza, ze proces w ogóle nie będzie korzystał z ***. Druga  – pasek jest potrzebny, ale proces nie będzie produkował żadnych ostrzeżeń i błędów. Natomiast ostatnia – pasek jest potrzebny i proces będzie produkował ostrzeżenia i błędy.

Teraz zaimplementujemy sam proces.

   1: internal class SharpedDaemonStageProcess : CSharpDaemonStageProcessBase
   2: {
   3:     public SharpedDaemonStageProcess(IDaemonProcess daemonProcess) 
   4:         : base(daemonProcess)
   5:     {
   6:     }
   7:  
   8:     public override void ProcessFile(ICSharpFile file)
   9:     {
  10:         file.ProcessDescendants(this);
  11:         this.FullyRehighlighted = true;
  12:     }
  13:  
  14:     public override void VisitThrowStatement(IThrowStatement throwStatementParam)
  15:     {
  16:         this.AddHighlighting(new ThrowHighlighting(throwStatementParam));
  17:     }
  18: }

Ograniczyłem się tu do zaprezentowania samego mechanizmu podświetlania, nie zaś analizy. Jedynym zadaniem powyższej implementacji jest podkreślenie wszystkich słów kluczowych throw.

Zanim jednak przejdę do omówienia tej implementacji chciałbym pokrótce omówić to jak R# działa (przynajmniej jak ja to rozumiem). Otóż z tego co wiem, to R# posiada swój własny parser języka C# (to tłumaczy trochę czas jaki potrzebują na adaptację do nowej wersji języka). Wynikiem działania tego parsera jest drzewo PSI, które możemy następnie analizować. Proces analizy w R# został oparty o wzorzec Wizytatora (ang. Visitor)zine.net.pl.resharper.plugins.03.process.base. Dlatego chcąc przeanalizować strukturę drzewa PSI należy uruchomić własnego wizytatora na tym drzewie. W powyższym przykładzie dziedziczymy po klasie CSharpDaemonStageProcessBase. Jest to klasa bazowa dla etapów działających dla języka C#. Na obrazku po pawej mamy hierarchię reprezentującą tę klasę. Jak łatwo zauważyć dziedziczy ona po klasie bazowej ElementVisitor, zatem jest wizytatorem elementów drzewa (każdy węzeł w drzewie implementuje interfejs IElement). Następnie mamy IDaemonStageProcess, a więc będzie też etapem. Nawiązując jeszcze do klasy ElementVisitor trzeba zaznaczyć, że definiuje ona szereg metod pozwalających odwiedzać poszczególne elementy drzewa. zine.net.pl.resharper.plugins.03.element.visitor

Analizę zaczynamy wywołując metodę ProcessDescendants na przekazanym nam pliku. W trakcie analizy nasz etap może dołączać swoje podświetlenia za pomocą metody AddHighlighting. Po zakończeniu etap przekazuje wszystkie podświetlenia oraz zakres dokumentu jaki został przeanalizowany (robi to za nas klasa bazowa). Zwykle jest to cały plik, dlatego także w naszym przypadku ustawiamy właściwość FullyRehighlighted na true. Możliwe są też bardziej zaawansowane scenariusze, w których analizie może podlegać tylko pewien fragment drzewa. Pozwala to uzyskać lepszą wydajność, jedka ja jeszcze nie znam szczegółów. zine.net.pl.resharper.plugins.03.throw.statement

Przed przystąpieniem do analizy drzewa PSI należy poznać jego strukturę. Jest to drzewo obiektowo reprezentujące strukturę kodu, zatem każdemu elementowi odpowiada pewna klasa. W naszym przykładzie chcemy analizować słowa kluczowe throw, a więc będziemy analizowali throw statements. Obrazek obok prezentuje tą właśnie klasę oraz całą hierarchię dziedziczenia. W tym miejscu należy podkreślić dwie ważne prawidłowości, które dotyczą wszystkich elementów. Każdy z nich implementuje dwa interfejsy. W naszym przypadku będą to IThrowStatement oraz IThrowStatementNode. Pierwszy z nich reprezentuje logiczny element, w tym przypadku rzucenie wyjątku za pomocą słowa kluczowego throw. Drugi z nich reprezentuje ten sam obiekt, ale w realiach drzewa PSI. Co więcej zauważmy, że IThrowStatementNode dziedziczy po IThrowStatement. Dla mnie na początku było to bardzo mylące i nie do końca łapałem strukturę. Szczególnie, gdy implementując wizytatora dostajemy np. interfejs IThrowStatement i patrząc na jego zawartość poprzez intellisense nie mamy elementów, których byśmy się spodziewali. Na szczęście każdy tego typu element możemy rzutować na odpowiadający mu typ *Node, albo najzwyczajniej wywołać na nim metodę ToTreeNode.

O ile interfejs logiczny dostarcza nam elementów składowych np. dla throw mamy właściwość Exception dającą nam dostęp do wyrażenia reprezentującego rzucany wyjątek, o tyle interfejs drzewkowy daje nam dostęp do poszczególnych elementów takich jak słowo kluczowe throw, wyjątek czy średnik.

Wracając do naszego przykładu, w którym podkreślamy słowa kluczowe throw widać, że implementujemy metodę VisitThrowStatement. Ta metoda zostanie wywołana dla każdego słowa kluczowego throw znajdującego się w analizowanym pliku. Nie przeprowadzamy tu żadnej analizy, po prostu dodajemy podświetlenie.

Podświetlenia (ang. highlightings) definiowane są jako dedykowane klasy dziedziczące po CSharpHighlightingBase i implementujące interfejs IHighlighting. Poniżej przykładowa implementacja.

   1: [StaticSeverityHighlighting(Severity.WARNING)]
   2: public class ThrowHighlighting : CSharpHighlightingBase, IHighlighting
   3: {
   4:     private IThrowStatement ThrowStatement { get; set; }
   5:  
   6:     public ThrowHighlighting(IThrowStatement throwStatement)
   7:     {
   8:         ThrowStatement = throwStatement;
   9:     }
  10:  
  11:     public override DocumentRange Range
  12:     {
  13:         get { return this.ThrowStatement.ToTreeNode().ThrowKeyword.GetDocumentRange(); }
  14:     }
  15:  
  16:     public string ErrorStripeToolTip
  17:     {
  18:         get { return "This is throw statement! (on a strip)"; }
  19:     }
  20:  
  21:     public int NavigationOffsetPatch
  22:     {
  23:         get { return 0; }
  24:     }
  25:  
  26:     public string ToolTip
  27:     {
  28:         get { return "This is throw statement! (tool tip)"; }
  29:     }
  30: }

Przeanalizujmy teraz ten przykład. W konstruktorze przyjmujemy IThrowStatement bo to będzie dla nas źródło danych. Następnie z klasy bazowej mamy do zaimplementowania abstrakcyjną właściwość Range. Właściowść ta powinna zwrócić obiekty typu DocumentRange reprezentujący zakres na drzewie, który powinien zostać podświetlony. Każdy element drzewa ma metodę GetDocumentRange(), która zwraca jego zakres na drzewie. Z tego co się orientuję to jest jeszcze zakres tekstowy, odpowiadający pozycji w edytorze i można go pobrać z właściwości TextRange obiektu DocumentRange. Jako, że w przykładzie chcemy podświetlać słowa kluczowe throw, DocumentRange pobieramy z właściwości ThrowKeyword.

W kolejnym kroku mamy za zadanie zwrócić odpowiedni opis dla naszego podświetlenia. Mamy trzy miejsca, w których opis będzie widoczny. Jest to boczny pasek ze znacznikami i po najechaniu znacznika reprezentującego dane podświetlenie wyświetli się zawartość ErrorStripeToolTip. Natomiast po najechaniu myszką na podświetlenie lub ustawienie tam kursora spowoduje wyświetlenie właściwości ToolTip.

Ostatnim elementem i chyba najrzadziej używanym jest NavigationOffsetPatch. Jest to offset (tekstowy), o który zostanie przesunięty kursor podczas nawigowania do tego podświetlenia. Co ciekawe zauważyłem, że offset ten zostanie zaaplikowany jak będziemy nawigować za pomocą komend np. „Go to next highlighting” natomiast nie jak klikniemy na pasek reprezentujący podświetlenie. Przykładowo w naszym przykładzie zwrócenie wartości 5 zaowocowałoby ustawieniem kursora za słowem kluczowym throw. Ciekawskich zachęcam do eksperymentów.

Ostatnim elementem jest atrybut znajdujący się nad klasą – StaticSeverityHighlighting. Atrybut ten definiuje nam podświetlenie, którego severity jest statyczne i nie można go modyfikować w opcjach. Konfigurowalnych podświetleń jeszcze nie rozczaiłem.

Takim oto sposobem mamy gotowe rozwiązanie podświetlające wszystkie słowa kluczowe throw. Nie jest to co prawda bardzo użyteczna analiza jednak mam nadzieję, że przykład dostatecznie pokazał Wam mechanizm podświetlania w R#. W złączniku znajduje się cały ten dodatek, który możecie sobie sami uruchomić i trochę z nim poeksperymentować. Pamiętajcie, aby w opcjach debugowania projektu ustawić odpowiednią ścieżkę do pliku z wtyczką tak jak to zostało opisane w części drugiej.

  1. Analiza kodu i podświetlanie (ten tekst)
  2. R#4.5 Beta i errata do trzeciego odcinka
opublikowano przez nuwanda | 3 komentarzy
Filed under: ,

Attachment(s): SharpedPlugin_3.zip

M-V-P Twoim przyjacielem

Będąc na studiach zacząłem pracę programisty. Głowę miałem wypakowaną teorią, a w duszy grała chęć zastosowania tego wszystkiego w praktyce. Każdy kto czytał o wzorcach projektowych GoF wie jak bardzo zmienia ona postrzeganie i jak bardzo zachęca nas do wykorzystywania tychże wzorców w praktyce. Niestety od nadmiaru wzorców głowa boli. Pewne mało skomplikowane aplikacje zupełnie ich nie potrzebują, a wprowadzenie ich tylko niepotrzebnie zaciemnia obraz sytuacji. Zatem wybór wzorca i to, czy rzeczywiście jego zastosowanie ma sens, poprzeć trzeba pewnym uzasadnieniem.

Zaczynając pracę trafiłem do zespołu, który buduje aplikację w oparciu o Windows Forms, znaczy się to co tygryski lubią najbardziej. Już na samą myśl o WF w głowie zapala mi się lampka - MVP. Wcześniej nie miałem okazji wypróbować go w boju. W małych projektach studenckich stosowałem, ale z rzeczywistych jego możliwości nigdy nie korzystałem. Co więcej, nie do końca zdawałem sobie sprawy z jego potęgi!

W tekście tym chciałem Wam opowiedzieć o tym jak na co dzień wykorzystuję ten wzorzec i jakie wymierne korzyści dzięki niemu uzyskałem.

Grunt to dobry podział

Dla wielu implementacja wzorca MVP sprowadza się do tego, że będą mogli podmieniać widoki. Casami ludzie mówiąc o zaletach MVP wskazują na to, że można tak napisać aplikację wykorzystując ten wzorzec, że będzie można wymiennie stosować Windows Forms oraz ASP.Net. Jakoś nie widziałem, żeby się to komuś udało. Nie twierdzę że nie jest to możliwe, ale czy rzeczywiście o to chodzi?

Spójrzmy może na ten wzorzec z innej strony. Wprowadza on podział na trzy elementy: Model, Widok i Prezentera. Zapytać ktoś może po co taki podział? Czemu nie mogę oprogramować okna w jego klasie? Nie mając perspektyw dalszego rozwoju aplikacji moglibyśmy tak zrobić, jednak praktyka sugeruje, że aplikacja będzie rosła a klient będzie wymyślał nowe problemy do rozwiązania.

Przede wszystkim oddziel zagadnienia (ang. Separation of Concerns, SoC). Wprowadzenie tych trzech elementów nie jest przypadkowe. Widok to reprezentacja wizualna danych – która może mieć wiele postaci np. okno czy strona www. Prezenter to logika reprezentacji danych. Definiuje zachowanie widoku np. reaguje na akcje użytkownika. Na końcu model to dane. Czasami danymi jest pojedynczy obiekt, innym razem może to być zbiór obiektów. Promując zasadę SoC rozdzielamy te zagadnienia umieszczając je w osobnych klasach.

Podmiany

Dobrze, mamy już jakieś wymierne korzyści, które w dowolnej wielkości aplikacji będą procentować. Zastanówmy się teraz jak z tego wzorca wyciągnąć jeszcze więcej. W pracy niejednokrotnie spotkałem się z problemem ponownego wykorzystania komponentów. Weźmy mały przykład, który z powodzeniem odnajdziecie w wielu rozwiązaniach klasy ERP, CRM lub podobnych. Chodzi mi o listę kontrahentów. Z pozoru problem jest prosty – należy umożliwić zarządzanie kontrahentami w aplikacji. Sprowadza się to do zaimplementowania listy kontrahentów, a także szczegółów konkretnego kontrahenta. Skupmy się jednak na samej liście. Zadanie banalne – mamy tabelę w bazie danych zawierającą dane o kontrahentach. Nasza lista będzie pobierała wszystkie dane z tej tabeli i wyświetlała w tabeli w oknie programu.

Mija czas i przychodzi następne wymaganie. Należy umożliwić wybór kontrahenta z listy. Sytuacja dotyczy wszystkich dokumentów handlowych aplikacji, w których trzeba wybrać kontrahenta. Oczywiście system musi być spójny więc lista musi wyglądać i zachowywać się identycznie (możliwość dodawania, edycji itd.). Zabieramy się za implementację. Tę samą formatkę wzbogacamy o właściwość IsInSelectionMode, którą ustawiać będziemy na true, gdy będziemy wyświetlać listę w trybie wyboru. Wewnątrz dodajemy odpowiednie if-y, aby zmienić działanie formatki np. jednym z wymagań jest to, aby dwuklik na wierszu powodował wybór w trybie wyboru, a edycję w trybie zwykłym. Wszystko śmiga.

Znów mija trochę czasu. Przychodzi klient i mówi: jak wpiszę tylko część nazwy klienta a w bazie jest więcej niż jeden klient o nazwie zaczynającej się na wpisaną frazę to chcę aby pojawiła się lista klientów zawężona tylko do tych pasujących do frazy. Znów przystępujemy do implementacji. Gdy użytkownik wpisze żądaną frazę strzelamy do bazy i mamy już listę pasujących kontrahentów. Zgodnie z warunkiem, jeżeli jest ich więcej niż jeden otwieramy listę. Sama lista też wymaga pewnych modyfikacji. Mając zestaw danych w ręce (listę pasujących kontrahentów) nie chcemy aby lista sama pobierała dane, tylko chcemy jej podać to co już mamy. Dodajemy kolejne pole i przypisujemy mu zawartość listy kontrahentów zaczynających się od frazy. W samej liście znów dodajemy if-y, tak aby spełnić wymagania. Teraz jeżeli nowo dodane pole nie jest puste nie pobieramy wszystkich kontrahentów z bazy, a jedynie wyświetlamy tych otrzymanych.

Spoglądając wstecz zauważymy, że stworzyliśmy coś, co na dłuższą metę może być uciążliwe w utrzymaniu. Nie dość, że wszystkie funkcje dotyczące obsługi listy są w jednej klasie to jeszcze sama logika się skomplikowała, ponieważ wprowadzono warunki if. Każda z komend dotycząca zawartości wyświetlanej listy np. ‘odśwież’ musiała zostać odpowiednio zmodyfikowana, aby zapewnić poprawność, bo przecież nie możemy pobierać danych z bazy danych mając zadaną stałą listę. To prowadzi do większego prawdopodobieństwa popełnienie błędu, albo wprowadzenia błędu przy późniejszych modyfikacjach.

Co więcej z punktu widzenia programisty, który naszej listy będzie używał, jej API nie do końca jest intuicyjne. Musi wiedzieć, że właściwość IsInSelectMode przełącza listę w tryb wyboru, a ustawienie pola ExternalDataSource powoduje zmianę zachowania listy i wyświetlenie tylko zadanego zestawu danych.

Rozwiązanie takie tworzy wiele problemów. Nie ma jasno powiedziane gdzie znajduje się źródło danych. Informacja ta jest rozproszona po kodzie całej kontrolki. Nie ma też jasno określonego dostawcy danych. Lista w standardowym trybie sama zaczytuje sobie dane, ale w przypadku trybu zawężonej listy, dane pobierane są z zewnątrz.

Z punktu widzenia utrzymania takiego komponentu mamy kolejny problem. Promując zasadę otwarty-zamknięty (ang. Open-Closed Principle, OCP) chcielibyśmy, aby dodawanie nowych funkcji mogło się odbyć bez modyfikacji już istniejących klas. W tym wypadku dodanie kolejnego trybu (a nie zdziwiłbym się, gdyby poproszono o coś jeszcze) wiąże się z nieustannym modyfikowaniem tej samej klasy, która szybko przyjmie rozmiary kilku tysięcy linii kodu.

W tym przypadku i wielu innych, które spotkałem podczas pracy z pomocą przyszedł mi wzorzec MVP i podział wprowadzany przez jego komponenty. Tak jak pisałem wcześniej Prezenter określa nam klasę odpowiedzialną za zachowanie, a model dostarcza danych. Z powodzeniem możemy wykorzystać te klasy jako elementy wymienne.

Zobaczmy jak takie rozwiązanie mogłoby wyglądać. W tekście wytłuściłem elementy opisujące kolejne wymagania. Pierwsza zmiana polegała na zmianie zachowania formatki. Skoro zmienialiśmy zachowanie to w modelu MVP wystarczyłoby wymienić prezentera na takiego, który definiuje odrębne zachowanie. Wystarczyłoby mieć bazowego prezentera, który obsługuje wszystkie wspólne komendy takie jak dodawanie i usuwanie, a dwa dziedziczące po nim definiowały by obsługę dwukliku. Prawda, że proste?

Drugie wymaganie polegała na zmianie danych wyświetlanych na liście. Dlaczego nie wyodrębnić dwóch modeli. Pierwszy obsługiwałby wersję standardową wczytując wszystkich kontrahentów z bazy danych, a drugi przyjmowałby w konstruktorze gotowy zestaw danych. Prawda, że proste?

Teraz uruchomienie listy w żądanym trybie sprowadza się do wyboru odpowiedniego prezentera oraz odpowiedniego modelu. Na przykład chcąc uruchomić listę w trybie wyboru z zawężoną listą kontrahentów napisalibyśmy:

1 var view = new CustomerListView(); 2 var model = new ManualCustomerListModel(matchingCustomers); 3 var presenter = new CustomerSelectionPresenter(view, model);

Oczywiście nie moglibyśmy tego osiągnąć gdyby nie to, że każdy z tych komponentów opisany został dobrze określonym interfejsem. Dzięki temu całą trójkę możemy dowolnie komponować.

Wspomniałem wcześniej, że pierwsze rozwiązanie w ogóle nie spełnia zasady otwarty-zamknięty. Zobaczmy jaką sytuację mamy teraz. Każda z wariacji na temat listy kontrahentów zamknięta została w swojej własnej klasie. Zaimplementowanie kolejnego przypadku sprowadza się teraz do rozważenia jakie własności są modyfikowane. Jeżeli modyfikujemy zachowania to należy odpowiednio zaimplementować prezentera. Natomiast jeżeli modyfikujemy wyświetlane dane wprowadzamy nową implementację modelu. Co ważne to to, że nawet przez chwilę nie dotykamy klas już istniejących.

Podsumowanie

Przedstawiony tutaj problem dotyczący listy kontrahentów jest oczywiście dość prostym przypadkiem. W rzeczywistości spotkałem się z sytuacjami, gdzie zachowanie okna znacznie się różniło od jego podstawowej implementacji. Tak samo było z danymi. Nie implementowałem natomiast nigdy dwóch widoków mających prezentować wspólne dane, ale jak łatwo się domyślić nie powinno to nastręczać problemów przy zachowaniu jednolitego interfejsu.

Ci, którym nie jest obojętna wysoka jakość systemu zauważą, że implementacja testów jednostkowych dla tak zdefiniowanego systemu, nie powinna być skomplikowana. Projektując w ten sposób sprawiamy, że system jest testowalny.

Z własnych praktycznych doświadczeń mogę szczerze powiedzieć, że zastosowanie wzorca MVP oszczędziło mi wiele pracy, oszczędza i będzie oszczędzało w przyszłości, gdy przyjdzie mi rozszerzać bądź poprawiać błędy. Mając odpowiedni podział możemy dowolnie manipulować jego podzespołami, wymieniając je wedle uznania i komponując co raz to nowe rozwiązania. Tak zaprojektowany system jest niesamowicie elastyczny, zgodny z zasadą OCP, dzięki czemu daje się łatwo utrzymywać.

Dobrze określony podział obowiązków pozwala nam szybciej i bardziej mechanicznie podejmować decyzje o tym gdzie dana funkcja powinna się znaleźć. Znając naturę dużych i skomplikowanych systemów wiem, że implementacja jednej formatki może nam urosnąć do kilku tysięcy linii kodu. Dzięki podziałowi możemy tą ilość podzielić.

Moja implementacja tego wzorca cały czas ewoluuje. Oparłem ją na wzorcach, które zdefiniował Martin Fowler. Passive View definiuje widok jako reprezentację graficzną, która jest „głupia” jeżeli chodzi o obsługę komend i do tego celu wykorzystuje prezentera. Supervising Controller określa prezentera jako sterownik, który odbiera komendy od widoku i zawiaduje całą logiką. Moją implementację staram się cały czas ulepszać. Jeżeli jeszcze tego nie robicie to zachęcam Was do eksperymentów i zaadaptowania tego wzorca. Zapewniam, że włożony w to wysiłek na pewno się zwróci. Na początku może się to wydawać niepotrzebnie skomplikowane i na wyrost, ale wraz z wzrostem systemu dostrzeżecie zalety.

Wtyczki do ReSharper 4.x – Odc. 2 – Przygotowanie środowiska

Przygodę z tworzeniem wtyczek zaczniemy od przygotowania środowiska programistycznego. Przede wszystkim potrzebna jest nam instalacja samego R#, bo to jego biblioteki są dla nas podstawą. Z tego co zauważyłem to R# nie ma osobnych bibliotek w stylu SDK. Tworząc wtyczki będziemy odwoływali się do tych bibliotek, które będą nam potrzebne.

Wtyczka dla ReSharpera to biblioteka DLL. W jednej bibliotece może znajdować się tylko jeden plugin. R# potrafi współpracować z wtyczkami napisanymi dla .Net 3.5 jak również 2.0. Zacząć należy od utworzenia projektu biblioteki (Windows Class Library). Następnie nowo powstały projekt konfigurujemy, aby móc w prosty sposób testować rozwijaną wtyczkę.

W opcjach projektu ustawiamy, aby przy wywołaniu debugowania uruchamiał się zewnętrzny program z odpowiednimi parametrami w lini poleceń. Oczywiście tym programem będzie Visual Studio jako że to w nim będzie osadzona nasza wtyczka. Do rejestracji wtyczki w R# na czas debugowania służy przełącznik:

/ReSharper.Plugin "ścieżka do pliku DLL wtyczki"

Całość może wyglądać tak: c:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\devenv.exe /ReSharper.Plugin "D:\projekty\ResharperPrugins\SampleResharperPlugin\bin\Debug\SampleResharperPlugin.dll”. Dzięki tym ustawieniom będziemy mogli debugować rozwiązanie w Visual Studio.

image

Rys. 1. Konfiguracja opcji debugowania

Drugim krokiem jest przygotowanie samej wtyczki tak, aby R# mógł ją odpowiednio rozpoznać. Do tego celu służą atrybuty, które możemy umieścić w pliku AssemblyInfo.cs. Aby móc z nich skorzystać musimy dodać referencję do pliku JetBrains.Platform.ReSharper.UI.dll. Do opisu wtyczki służą atrybuty: PluginTitleAttribute, PluginDescription, PluginVendor. Myślę, że ich nazwy mówią same za siebie. Każdy z nich przyjmuje jako parametr napis określający odpowiednią wartość. Przykładowe użycie może wyglądać tak:

  1 [assembly: PluginTitle("Szarpnięty!")]
2 [assembly: PluginDescription("Wyszarpana wtyczka")]
3 [assembly: PluginVendor("Bartłomiej Legiędź")]

Teraz wciskając F5 po raz pierwszy uruchomimy swoją własną wtyczkę do R#. Chwilowo zignorujemy fakt, że ta wtyczka nic nie robi. Zadowolimy się tym, że R# już o niej wie.

zine.net.pl.resharper.plugins.02.loaded 
Rys. 2. Wtyczka została rozpoznana i załadowana

Podsumowując, przygotowanie środowiska jest proste i zamyka się w następujących krokach:

  1. Tworzymy projekt Windows Class Library
  2. Konfigurujemy opcje debugowania
  3. Dodajemy referencję do JetBrains.Platform.ReSharper.UI.dll
  4. Dodajemy atrybuty opisujące wtyczkę (nazwa, opis, producent)
  5. Wciskamy F5 by sprawdzić, czy wtyczka poprawnie się ładuje

Dla leniwców przygotowałem mały wzorzec projektu dla Visual Studio. Dołączone do postu archiwum wystarczy skopiować do katalogu z wzorcami projektów (domyślnie: c:\Documents and Settings\<user>\Moje dokumenty\Visual Studio 2008\Templates\ProjectTemplates\Visual C#\) i VS pozwoli nam z tego wzorca skorzystać. Zawarłem w nim referencję do biblioteki R# zawierającej atrybuty wtyczki, a w pliku AssemblyInfo.cs umieściłem te atrybuty z pustymi wartościami.

Spis treści:

  1. Wprowadzenie
  2. Przygotowanie środowiska (ten tekst)
  3. Analiza kodu i podświetlenie
  4. R#4.5 Beta i errata do trzeciego odcinka
opublikowano przez nuwanda | 8 komentarzy
Filed under: ,

Attachment(s): ReSharper Plugin.zip

Wtyczki do ReSharper 4.x – Odc. 1 – Wprowadzenie

Będąc jeszcze na studiach byłem na jednym z seminariów, które prowadził mój kolega z roku niżej. Podczas prezentacji pisał kod ‘na żywo’. Nie dość, że i tak już szybko pisał na klawiaturze to okazało się, że używa jeszcze jakichś czarodziejskich sztuczek. W oknie edytora pojawiały się jakieś dziwne kółka i strzałki. Po westchnięciach sali przerwał i przedstawił narzędzie. Był to dodatek do Visual Studio - CodeRush + Refactor! Pro. Był to rok 2004.

Po zainstalowaniu tego narzędzia szybko okazało się, że mój komputer sobie z nim nie poradzi. Jeszcze tego samego dnia odinstalowałem je i trochę zawiedziony usiadłem do ‘starego’ Visual Studio. W między czasie pojawiały się nowe wersje, a także konkurencja (nie wiem co było pierwsze Refactor! czy ReSharper, ale to nie ważne). Faktem jest, że nie zawracałem sobie nimi głowy, bo po co przyzwyczajać się do jakiegoś narzędzia, na które mnie nie stać i które po 30 dniach trzeba będzie odinstalować...

Jednak ostatnio ReSharper znów wpadł mi w ręce. Pojawiły się nocne buildy i przez dłuższy okres czasu można było mieć go za darmo. Spróbowałem i okazało się, że po kupnie dodatkowego 1Gb ramu (po tym w sumie mam 2Gb) mogę spokojnie z nim pracować. Okazało się też, że po tygodniu współpracy już nie mogłem bez niego żyć ;)

I został ReSharper. Jestem z niego bardzo zadowolony. Z jednej strony traktuję to narzędzie jako programistyczny dopalacz, który przyspiesza moją pracę. Z drugiej jednak strony wydaje się on użytecznym zamiennikiem API, którego dostarcza nam Visual Studio. W serii artykułów chciałbym Wam przybliżyć Open API, którego dostarcza ReSharper. Osobiście widzę w tym narzędziu wielki potencjał. Kolejnymi artykułami chciałbym zachęcić Was do tego, aby ten potencjał wyokorzystać. Nadmienić należy również, że wiele funkcji ReSharpera jest pisana tak jak dodatki, czyli przy użyciu Open API.

Tyle tytułem wstępu. Jeżeli ktoś z Was chciałby tak jak ja zacząć przygodę z dodatkami do ReSharpera na pewno zacząłby od googla, a ten skierowałby go prosto na strony JetBrains dotyczącą tego właśnie zagadnienia. Jak wszyscy dobrze wiemy z autopsji, dokumentacji nikt nie lubi pisać. Widać, że chłopaki z JetBrains mieli dobry start, bo do wersji 2.5 jest przynajmniej wstęp do pisania dodatków. Do wersji 3.0 są tylko PowerToy’e obrazujące wykorzystanie niektórych funkcji API. Do wersji 4.0 nie ma jeszcze nic (na forum ReSharpera pojawiły się już informacje, że PowerToy’e zostały już zmigrowane do najnowszej wersji i jak tylko przejdą proces weryfikacji to pojawią się do ściągnięcia).

Brak dokumentacji do najnowszej wersji wcale mnie nie zniechęcił. Mamy przecież Reflectora, a w razie niejasności zawsze możemy popytać na forum. Niestety okazuje się też, że od wersji 2.5 sporo się zmieniło. Podczas przerabiania PowerToy z wersji 3.0 na wersję 4.0 okazało, że niektóre typy zniknęły, a pewne interfejsy zostały uproszczone. To oczywiście dobrze, bo widać, że w źródłach robione są pewne porządki. Ostatnio na swoim blogu Ilya Ryzhenkov pisał o kolejnych krokach w rozwoju narzędzia. Możemy się zatem spodziewać kolejnych zmian.

Dostępnych pluginów nie jest zbyt dużo. Mamy dziewięć oficjalnych na stronie JetBrains, a ponadto możemy znaleźć jeszcze kilka na stronach CodePlex, CodeProject itd. Jednak nie jest to duża liczba. W Sieci nie znajdziemy również wielu tekstów dotyczących tworzenia dodatków. Najwyższy więc czas zmienić ten stan rzeczy!

Spis treści:

  1. Wprowadzenie (ten tekst)
  2. Przygotowanie środowiska
  3. Analiza kodu i podświetlenie
  4. R#4.5 Beta i errata do trzeciego odcinka
opublikowano przez nuwanda | 9 komentarzy
Filed under: ,

TfsSpotlight odsłona piąta (v0.5)

Kilka dni temu wydałem kolejną wersję mojego małego projektu. W piątej iteracji dodałem możliwość tworzenia nowych jednostek roboczych (ang. work item) oraz zmieniłem sposób zarzadzania poszczególnymi serwerami.

Dla tych, którzy nie wiedzą czym jest TfsSpotlight:

  • Jest to aplikacja wykorzystująca API Team Foundation Server.
  • Pozwala na przeglądanie oraz modyfikację jednostek pracy.
  • Poszczgólne zapytania oraz konkretne jednostki pracy można przeglądać jednocześnie dzięki zakładkom.
  • Jest niezależne od Visual Studio i dzięki temu można niezależnie np. pobierać wersję z serwera i przeglądać jednostki pracy.
  • Ułatwia szybkie znalezienie jednostki pracy dzięki funkcji GoTo.
  • Jest ergonimiczny, co pozwala skupić się na wykonywanej pracy a nie na niedociągnięciach aplikacji.

Przede mną kolejna iteracja a w niej:

  • Historia jednostek pracy, których dotyczyły nasze operacje (tworzenie, modyfikacja).
  • Udogodnienia w dostępie do projektów.

Aplikacja jest dostępna jak zwykle w postaci archiwum zip, instalatora windows oraz pakietu autoaktualizacji (dla tych co już mają ją zainstalowaną).

Udanej pracy!

TfsSpotlight v0.5

opublikowano przez nuwanda | 28 komentarzy
Filed under:

Nocny ReSharper 4.0

Odkąd zacząłem używać Resharpera moje dotychczasowe życie programisty zmieniło się nie do poznania. Nie jest to bynajmniej długa historia, ale faktem jest, że odbiła się piętnem na mojej codziennej pracy.

W lutym wystartował proces nocnych buildów nowej wersji Resharpera. Buildy te są publicznie dostępne. Każdy kolejny build zawiera nową 30-dniową licencję. Dzięki temu mogę korzystać z tego wspaniałego narzędzia i cieszyć się zaoszczędzonym czasem (przynajmniej do czasu premiery tej wersji). Później nie pozostanie nic innego jak zakupić licencję dla siebie. Co ciekawe warunki licencji personal pozwalają na używanie Resharpera zarówno w domu jak i w pracy. Warunkiem jest to, żeby używała go osoba, która zakupiła licencję. Cieszy mnie to bardzo, bo dzięki temu R# będzie mógł być zawsze blisko mnie ;). A skoro w planach mam taki zakup to nikogo nie zdziwi fakt, że zależy mi na tym, aby powstał produkt wysokiej jakości. Zarejestrowałem się więc w ich systemie śledzenia błędów i tam właśnie lądują wszystkie moje uwagi i znalezione błędy. Was również zachęcam do czynnego udziału.

Dwa miesiące pacy z wersją 4.0 i ponad miesiąc z wersją 3.0 i jestem zachwycony, żeby nie powiedzieć uzależniony. Tym co jeszcze nigdy nie spróbowali, a pracują w środowisku VSC#, polecam gorąco. Tym co używają wersji wcześniejszej polecam wypróbowanie któregoś z nocnych buildów wersji 4.0. A ja zadaję sobie pytanie: czemu tak późno zacząłem tego używać?!

Thank you JetBrains!

opublikowano przez nuwanda | 23 komentarzy
Filed under:

Biblioteka dostępu do TFS i testy jednostkowe

W poście Tfs Spotlight – buduję własny CAB wprowadzającym do mojego projektu TfsSpotlight wspomniałem, że jednym z moich celów jest pisanie testów jednostkowych. Chcę w ten sposób zobaczyć jakie problemy pojawią się podczas pisania testów jednostkowych dla większego i bardziej skomplikowanego projektu niż te, które do tej pory robiłem.

Proces pisania testów jednostkowych nie jest łatwy i co jakiś czas napotykam pewne problemy. Ostatni problem pojawił mi się w momencie, gdy chciałem napisać test dla klasy prezentera, który obsługuje widok konkretnego workitema. Źródłem danych dla tego widoku jest obiekt klasy pochodzącej z biblioteki dostępu do TFS – klasa WorkItem. Jak się szybko okazało napisanie testu jest niemożliwe, jeżeli bierzemy przypadek całkowitego odizolowania. Wszystko to dlatego, że nie mogę ręcznie utworzyć instancji tej klasy. Do tego potrzebne jest połączenie z serwerem (sic!). Moje zamiary spełzły na niczym. W dodatku klasa ta jest sealed, więc Rhino Mocks robie nie radzą. Nie mam zielonego pojęcia jak sobie z tym poradzić.

Wracając do biblioteki TFS pozwalającej na dostęp do serwera to należy zauważyć, że programiści i projektanci tego API zabezpieczyli się przed nieumiejętnym jego wykorzystaniem, aby zapobiec nadmiernemu obciążeniu serwera. Elementy takie jak Project, Query, WorkItem czy ich kolekcje są zaimplementowane w postaci klas, które swoje dane ładują w sposób leniwy. Tym sposobem wyświetlając w tabeli workitemy, pobierane są tylko właściwości wyświetlane w kolumnach. Pozostałe nie zostaną ściągnięte. Takie zachowanie na pewno ogranicza ilość danych przesyłanych z serwera, ale niestety sposób wykonania tych elementów może pozostawiać wiele do życzenia. Wszystkie te klasy o których mowa są sealed, a serwisy nie zostały opisane żadnymi interfejsami definiującymi kontrakty. Wygląda na to, że w tym przypadku nie pomyślano o Zasadzie oddzielenia zagadnień (ang. Separation of Concerns). Podobnie jest z kontrolkami dostarczanymi w bibliotece TFS. Okazuje się, że podpinając im źródło danych one i tak pytają o coś serwer (sic!)

Budując system luźno powiązany praca z takimi komponentami jest bardzo uciążliwa, a w tym przypadku skutecznie uniemożliwia mi pisanie testów. Chociaż trzeba zaznaczyć, że jest światełko w tunelu. Od jakiegoś czasu co raz częściej słyszy się o co raz to nowszych projektach ze stajni MS, które wspominają o testach jednostkowych. Ba, wczoraj Scott Guthrie ogłosił, że Silverlight 2 jest testowany przy pomocy testów jednostkowych (jest ich ponad 2000) i co więcej dostarczają również narzędzi do testowania własnych projektów Silverlight-owych.  Bardzo mnie ten trend cieszy, gdyż może następna wersja biblioteki dostępu do TFS będzie ten trend uwzględniała.

opublikowano przez nuwanda | 15 komentarzy

Globalizacja aplikacji i wątki

Natknąłem się na pewne zachowanie .Net Frameworka, które było zupełnie odmienne od tego, którego się spodziewałem. Problem dotyczy globalizacji i wątków. Okazuje się, że mając aplikację, która jest zlokalizowana na wiele języków musimy zwrócić szczególną uwagę za każdym razem gdy korzystamy z wątków.

Ustawienia dotyczące kultury są właściwościami wątku. W systemie Windows przy starcie wątku ustawienia kultury pobierane są z ustawień systemowych. Zatem jeżeli uruchamiamy aplikację, jej watek otrzymuje ustawienia użytkownika. Działanie to jest jak najbardziej oczekiwane. Okazuje się jednak, że tworzone w aplikacji kolejne watki otrzymują ustawienia kulturowe w taki sam sposób jak wątek pierwszy. Może nie było by w tym nic złego, ale osobiście oczekiwałem zachowania odwrotnego – czyli że nowy wątek otrzyma takie same ustawienia jak wątek główny. Natomiast gdy w międzyczasie zmienimy ustawienia kulturowe pierwszego wątku to pojawiają się dodatkowe problemy.

Wyobraźmy sobie aplikację, która przed uruchomieniem wyświetla okno, w którym możemy wybrać język w jakim chcemy, żeby się uruchomiła. Wybierając język inny niż ten, który znajduje się w ustawieniach systemu, będziemy działać swobodnie dopóki nie aplikacja nie zacznie korzystać z wątków.

Ja, będąc niedoświadczonym programistą, założyłem, że skoro aplikacja ma określone ustawienia kulturowe w pierwszym wątku, to tworząc nowe wątki ustawienia te zostaną zachowane (skopiowane z wątku, który tworzy nowe watki). Okazuje się jednak, że CLR nie zawiera żadnego mechanizmu kontrolowania ustawień kulturowych wątków tworzonych w danym procesie. Dlatego każdy tworzony wątek będzie miał ustawienia te pobrane z systemu Windows.

Dobrze, skoro już wiemy o tych niuansach, to trzeba się do tego przystosować. Jeżeli ręcznie tworzymy wątki to nie ma z tym żadnego problemu. Klasa Thread ma odpowiednie właściwości, dzięki którym możemy ustawienia kulturowe ustawić według własnego uznania. Ja jednak korzystam zwykle z puli wątków (klasa ThreadPool). Jak się szybko okazało klasa ta nie ma żadnego bezpośredniego wsparcia dla uruchamiania wątków z innymi ustawieniami kulturowymi. A szkoda. Skoro reguły są jasne, to powinniśmy chociaż dostać jakieś przeciążenie metody QueueUserWorkItem pozwalające określić ustawienia kulturowe uruchamianego wątku. A tak trzeba się tym ręcznie bawić i określać ustawienia kulturowe już w metodzie, uruchomionej w osobnym wątku, co moim zdaniem tylko wprowadza niepotrzebne zamieszanie w kodzie. Uważam, że można było tego uniknąć udostępniając odpowiednie API.

Podsumowując okazuje się, że pracując z aplikacją, która ma być lokalizowana na inne języki musimy szczególną uwagę zwrócić na obsługę wątków i odpowiednio ustawiać im właściwości kulturowe. Niemniej jednak zachowanie to jest dla mnie zgoła nie intuicyjne. Bo przecież zwykle chcemy, aby cała aplikacja działała z tymi samymi ustawieniami kulturowymi, a przypadki odwrotne są raczej sporadyczne. A może jest jakieś dobre uzasadnienie tego stanu rzeczy?

opublikowano przez nuwanda | 13 komentarzy
Filed under:
Więcej wypowiedzi Następna strona »