wtorek, 26 czerwca 2012

Co jest w Microsoft.VisualBasic

Microsoft.VisualBasic jest niedocenianą biblioteką. Ma bardzo dużo mechanizmów jakich standardowo potrzebuję. W tym poście postaram przedstawić ci te najciekawsze informację o niej.

W Microsoft.VisualBasic znajdują się typy związane z datą. To między innymi FirstDayOfWeek czy DateInterval. Już kilka razy pisałem enum z dniami tygodnia. Inne typy to m.in. TriState

Statyczne klasy Information oraz Interaction mają bardzo przydatne metody.
var isArray1= Information.IsArray(new int[2]); //true
var isArray2 = Information.IsArray(new ArrayList()); //false

var isDBNull = Information.IsDBNull( null); //false

var isReference1 = 
    Information.IsReference(new Int32()); //false

var isReference2 = 
    Information.IsReference(new string('a', 2)); //true

var getRGB = Information.RGB(255, 127, 0); //32767

var isNumeric1 = Information.IsNumeric("123.4"); //true
var isNumeric2 = Information.IsNumeric("123.4 num"); //false

var getTypeName = Information.TypeName(
                 new FieldAccessException());
                 //FieldAccessException

Interaction.Beep();
Interaction.InputBox("Info", "Title");

string decadeRange = 
        Interaction.Partition(80, 0, 99, 10); //" 80: 89"

Zawsze potrzebowałem metody, która sprawdzi mi czy dana zmienna jest DBNull. Interesowało mnie czy jest jakieś okienko z textboxem do wpisania tekstu. Teraz już wiem, że można skorzystać z Interaction.InputBox. Nietypowa metoda jest Interaction.Partition, która zwraca przedział.

Mamy możliwość operacji na stringu, gdzie standardowo w BCL nie było takiej możliwości. Możemy m.in. usuwać wolną przestrzeń tekstu z dowolnej strony.
string text = " te st ";
var r1 = Strings.LTrim(text);
var r2 = Strings.Trim(text);
var r3 = Strings.RTrim(text);

var rightChars = Strings.Right(text,3);
Co ciekawe, w tej biblioteczce znajduje się klasa odpowiedzialna za parsowanie pliku tekstowe. Jest nią TextFiledParser. Za pomocą tej klasy można w prosty sposób parsować plik CSV. Jeżeli będziesz chciał skopiować katalog do innego folderu to będziesz mógł wykorzystać metodę FileSystem.CopyDirectory albo FileSystem.DeleteDirectory, aby usunąć katalog. W tej bibliotece jest jeszcze wiele innych interesujące możliwości, choćby nawet możemy pobrać informacje o komputerze (Devices.ComputerInfo().TotalPhysicalMemory) czy możemy pingować (Network.Ping).
Ta klasa ma jeszcze jedna, bardzo interesująca możliwość. Możemy uruchomić aplikację w postaci singletona.

Testy jednostkowe dla Expression

Pisałem test dla wyrażeń lambda i to co mnie zaskoczyło to test, który nie przechodził dla zmiennych typu epression.
[Test]
public void TestExpressionPlusOne()
{
    Expression<Func<int, int>> increase1 = x => x + 1;
    Expression<Func<int, int>> increase2 = x => x + 1;

    Assert.AreEqual(increase1 , increase2 );
}
Test nie przechodził, gdyż zmienne do porównania są anonimowymi delegatami. Należało skompilować wyrażenie, gdyż CLR próbuję w locie stworzyć nową klasę. Wtedy uzyskałem do porównania dwa skompilowane wyrażenia. Podając argumenty mogłem dostać wartości dla których można porównywać te wyrażenia. Dla łatwiejszej implementacji wykorzystałem atrybut Values, który wypełniał parametr w funkcji.
[Test]
public void TestExpressionPlusOne( 
    [Values(-99,-9,-1,0,1,9,99)] int values)
{
    Expression<Func<int, int>> increase1 = x => x + 1;
    Expression<Func<int, int>> increase2 = x => x + 1;

    Func<Expression<Func<int, int>>, int> 
compileAndInvoke = expression => expression
                                .Compile()
                                .Invoke(values);
 
    Assert.AreEqual(compileAndInvoke (increase1 ), 
                    compileAndInvoke (increase2 ));
 
}
W taki sposób utworzyło się 7 testów dla różnych argumentów wyrażenia.


Testy dla DateTime.Now

Załóżmy, że mamy klasę, w której podajemy dowolną datę i w której jest metoda zwracająca różnicę w ilościach dni między tą datą, a dzisiejszą datą. W implementacji takiego zadania stosujemy statyczną właściwość DateTime.Now. To pole przy każdym wywołaniu ma inną wartość. Problem może pojawić się kiedy będziemy musieli przetestować metodę zwracającą ilość dni (GetDaysFromNow), która w różnych momentach zwraca inny wynik.
    public class NowDateTimeCalculator
    {
        public  DateTime dateTime { get; private set; }
        public NowDateTimeCalculator(DateTime dateTime)
        {
            this.dateTime = dateTime;
        }
 
        public int GetDaysFromNow()
        {
            var timeSpanDiff = DateTime.Now - dateTime;
            return (int)timeSpanDiff.TotalDays;
        }
    }
Mamy kilka sposobów, aby mieć możliwość przetestowania tej klasy. Pierwsza możliwość to zastosowanie pośredniej klasy (interfejsu), która mogłaby dostarczyć informacji o aktualnej dacie. Tworzymy interfejs zwracający datę (Now) oraz klasę implementującą to pole.
    public interface INowDateTime
    {
        DateTime Now { get; }
    }
    public class NowDateTime : INowDateTime
    {
        public DateTime Now
        {
            get { return DateTime.Now; }
        }
   }

Jeszcze będziemy musieli zmienić naszą klasę NowDateTimeCalculator. Parametr z interfejsem będziemy wprowadzać przez konstruktor. To nam uprości pisanie testów.
public class NowDateTimeCalculator
{
    public  DateTime dateTime { get; private set; }
    private INowDateTime nowDateTime;

    public NowDateTimeCalculator(
                               DateTime dateTime,
                               INowDateTime iNowDateTime)
    {
        this.dateTime = dateTime;
        nowDateTime = iNowDateTime;
    }
 
    public int DaysFromNow2()
    {
        DateTime now = nowDateTime.Now;
        var timeSpanDiff = now  - dateTime;
        return (int)timeSpanDiff.TotalDays;
    }
}
Wywołanie wygląda następująco:
var dateTimeToCheck= new DateTime(2012, 06, 26);

var calc = new NowDateTimeCalculator(
dateTimeToCheck, new NowDateTime());

var fromNow= calc.GetDaysFromNow();
W łatwy sposób będziemy mogli zamokować interfejs INowDateTime. Test może wyglądać następująco:
[Test]
public void Check_if_GetDaysFromNow_Work_With_Mock()
{
    DateTime date_26_06_2012 = new DateTime(2012, 06, 26);
    var nowDateTimeMock = new Mock<INowDateTime>();

    nowDateTimeMock
               .Setup(x => x.Now)
               .Returns(date_26_06_2012);

    var calcToday =
 new NowDateTimeCalculator(date_26_06_2012,
                           nowDateTimeMock.Object);
 
   Assert.AreEqual(0, calcToday.GetDaysFromNow());

    var calcTomorrow =
 new NowDateTimeCalculator(date_26_06_2012.AddDays(1),
                           nowDateTimeMock.Object);
 
   Assert.AreEqual(-1, calcTomorrow.GetDaysFromNow());

    var calcYesterday =
 new NowDateTimeCalculator(date_26_06_2012.AddDays(-1),
                           nowDateTimeMock.Object);

    Assert.AreEqual(1, calcYesterday.GetDaysFromNow());
}
Takie rozwiązanie jest łatwe w implementacji i w testowaniu, ale ma 2 ważne wady - tworzymy zależności od interfejsu INowDateTime oraz takie rozwiązanie musi być znane dla całego zespołu.


Innym rozwiązanie jest zastosowanie statycznej klasy.
internal static class SystemTime
{
    internal static Func<DateTime> 
                SetDateTimeNow = () => DateTime.Now;
 
    internal static DateTime Now
    {
        get { return SetDateTimeNow();}
    }
}


public class NowDateTimeCalculator
{
    public  DateTime dateTime { get; private set; }

    public NowDateTimeCalculator(DateTime dateTime)
    {
        this.dateTime = dateTime;
    }
 
    public int GetDaysFromNow()
    {
        DateTime now = SystemTime.Now;
        var timeSpanDiff = now  - dateTime;
        return (int)timeSpanDiff.TotalDays;
    }
}
Testy wyglądają następująco:
[Test]
public void Check_if_GetDaysFromNow_Work_With_SystemTime()
{
    DateTime date_26_06_2012 = new DateTime(2012, 06, 26);

    SystemTime.SetDateTimeNow =
               () => date_26_06_2012;
    var calcToday = 
        new NowDateTimeCalculator(date_26_06_2012);
    Assert.AreEqual(0, calcToday.GetDaysFromNow());


    SystemTime.SetDateTimeNow = 
              () => date_26_06_2012.AddDays(2);
    var calcTomorrow =
    new NowDateTimeCalculator(date_26_06_2012.AddDays(1));

    Assert.AreEqual(1, calcTomorrow.GetDaysFromNow());


    SystemTime.SetDateTimeNow = 
              () => date_26_06_2012.AddDays(-2);
    var calcYesterday =
    new NowDateTimeCalculator(date_26_06_2012.AddDays(-1));

    Assert.AreEqual(-1, calcYesterday.GetDaysFromNow());
}
Takie rozwiązanie pozwala na przyjemniejsze testowanie naszej klasy. Podoba mi się łatwa zmiana aktualnej daty poprzez wyrażenie lambda. 'Wadą' może być to, że korzystamy z globalnej i statycznej klasy, która musi być znana dla całego zespołu.


Jest jeszcze jedno rozwiązanie tego problemu. To rozwiązanie jest znane tylko dla osoby, która pisze testy. Zastosujemy porównywanie wartości uwzględniając przy tym błąd(margines) w wartościach. Do tego potrzebny nam będzie interfejs z informacją o marginesie.
    public interface IMarginComparer<T> 
    {
        T CompareMargin { get; }
    }
Dla naszych testów będziemy musieli zaimplementować dwie porównywające klasy. Porównamy wartości dla typu DateTime oraz int.
public class DateTimeComparer : 
             IComparer<DateTime>, 
             IMarginComparer<TimeSpan>
{
    public DateTimeComparer(TimeSpan marginOfDateTime)
    {
        this.CompareMargin = marginOfDateTime;
    }
 
    public int Compare(DateTime x, DateTime y)
    {
        var margin = x - y;
        if (margin <= CompareMargin)
        {
            return 0;
        }
        return new Comparer(CultureInfo.CurrentUICulture)
                                       .Compare(x, y);
    }
 
    public TimeSpan CompareMargin
    {
        get; private set; 
    }
}
 



public class IntComparer : 
             IComparer<int>, 
             IMarginComparer<int>
{
    public IntComparer(int margin)
    {
        CompareMargin = margin;
 
    }
    public int Compare(int x, int y)
    {
        var diff = x - y;
        if (diff <= CompareMargin)
        {
            return 0;
        }
        return new Comparer(CultureInfo.CurrentUICulture)
                                       .Compare(x, y);
    }
    public int CompareMargin { get; private set; }
} 
Mając margines możemy określić jaka może być różnica między aktualną, a oczekiwaną wartością. Aby obliczyć ten margines będziemy musieli znać datę pisania testu (dateTimeCodeWritten) i na podstawie tej daty obliczamy ilość dni na różnicę. Gdybyśmy tą różnicę nie obliczyli, a wpisalibyśmy stałą wartość to mogłoby się okazać, że któregoś dnia wszystkie nasze testy przestałyby przechodzić.
[Test]
public void Check_if_GetDaysFromNow_Work_With_Comparer()
{
var dateTimeCodeWritten = new DateTime(2012, 06, 25);
DateTime date_20_06_2012 = new DateTime(2012, 06, 20);

var calc = new NowDateTimeCalculator(date_20_06_2012);

var dateTimeCodeWrittenDiffTimeSpan =
 DateTime.Now - dateTimeCodeWritten;

int dateTimeDiffDays = 
(int)dateTimeCodeWrittenDiffTimeSpan.TotalDays;



var dateTimeComparer = 
new DateTimeComparer(new TimeSpan(dateTimeDiffDays, 0, 0,0));

Assert.IsTrue(dateTimeComparer
.Compare(calc.dateTime.AddDays(5), DateTime.Now) == 0);


var intComp = new IntComparer(dateTimeDiffDays);

Assert.IsTrue(intComp
.Compare(calc.GetDaysFromNow(), 5) == 0);  
   
}
Nie jest to najlepiej napisany test, ale można przetestować DateTime.Now. Zaletą tej techniki jest to, że nie musimy znać (zmieniać) implementacji kodu.

Te rodzaje rozwiązań można zastosować dla wielu innych statycznych metod w tym danych środowiskowych takich jak Environment.MachineName czy Thread.Sleep().

poniedziałek, 25 czerwca 2012

Double są szalone

Typ podwójnej precyzji zawarty w .NET jest bardzo dziwny. Zapisując liczbę mamy tak naprawdę liczbą zaburzoną pewnym błędem reprezentacji.
Poniższe przykłady przedstawiają "dokładność" metody zaokrąglającej oraz prostą operację dodawania.
double res0 = Math.Round(0.500000000000000000000000000D); //0
double res1 = Math.Round(1.500000000000000000000000000D); //2
double res2 = Math.Round(2.500000000000000000000000000D); //2
double res3 = Math.Round(3.500000000000000000000000000D); //4
double res4 = Math.Round(4.500000000000000000000000000D); //4
double res5 = Math.Round(5.500000000000000000000000000D); //6
double res6 = Math.Round(6.500000000000000000000000000D); //6
W komentarzach jest informacja co metoda zwraca.
double d = 0D;
decimal m = 0M;
 
double growD = 0.1D;
decimal growM = 0.1M;
for (int i = 0; i < 10; i++)
{
    d += growD;
    m += growM;
    Console.WriteLine("double :" + d.ToString("R"));
    Console.WriteLine("decimal:" + m.ToString("G"));
    Console.WriteLine();
}
 
A wynik prostego dodawania przedstawiony jest poniżej:



Jak widać na doublach można się mocno przejechać.

wtorek, 19 czerwca 2012

Komendy Finder

Korzystam z maca i niestety muszę korzystać z Findera. Na moje nieszczęście jestem na niego skazany. Oczywiście mogę zainstalować takie aplikacje jak Path Finder, Xfile, BBEdit, Smultron czy Disk Order. Wszystkie te pozycje są płatne ( kosztują odpowiednio 40$, 59$, 49.99$, 4.99$, 29.95$ - już wiem dlaczego programiści od maca lepiej zarabiają). W końcu znalazłem zastępnik Findera tak, aby był bezpłatny. Jest nim muCommander (odpowiednik TotalCommander pod maciem). Ale skoro już wylałem tyle łez, nie przespałem wiele nocek i wydłubałem sobie klawisze, to pozwoliłem sobie na napisanie krótkich wskazówek na temat Findera.



Wszystkie komendy zawarte w tych wskazówkach należy wywołać w terminalu. Po wywołaniu danej komendy należy uruchomić ponownie Findera (przytrzymaj klawisz opcji i prawym przyciskiem kliknij na ikonkę Findera, która znajduje się w docku).

1) Finder nie pokazuje ukrytych folderów i plików
Jeżeli jesteś użytkownikiem Findera i miałeś coś wspólnego z Unixem to z pewnością wiesz, że Finder nie wyświetla plików ukrytych (nazwa plików zaczyna się od . tak jak .bashrc) oraz niektórych folderów (m.in /usr, /bin, /etc i takie tam)
defaults write com.apple.finder AppleShowAllFiles TRUE
Możesz również pobrać AppleSkrypt, który wykonuje tą komendę za ciebie.

2) Ścieżka katalogu w tytule

defaults write com.apple.finder _FXShowPosixPathInTitle -bool YES


3) Zamykanie Findera
Findera nie da się zamknąć, gdyż on pracuje cały czas w tle. Aby móc zamknąć Findera wywołaj tą komendę:
defaults write com.apple.Finder QuitMenuItem -bool YES
Po każdym wykonaniu tych komend pamiętaj, aby uruchomić ponownie Findera. Mam nadzieję, że ten post był dla ciebie przydatny.

P.S. Tej post miał nazywać się F**k Finder

Czy goto ma zastosowanie w c#

Goto to zło. Ale w niektórych sytuacjach przydaje się.



Aby zobrazować dobre wykorzystanie goto przedstawię ci kod. 2 razy iterujemy po macierzy. Jeżeli znajdziemy element, który ma wartość 0 to kończymy tą podwójną pętle. Do tego będziemy potrzebować przerwania pierwszej pętli oraz zmienną informującą o tym czy zostało dokonane przerwanie. Przydałaby się instrukcja doublebreak, która mogłaby zakończyć dwie pętle :/

            int[,] ints = new int[2, 2];
            for (int i = 0; i < 2; i++)
            {
                bool breakSet = false;
                for (int j = 0; j < 2; j++)
                {
                    if (ints[0, 0] == 0) //check if is zero
                    {
                        breakSet = true;
                        break;
                    }
                }
                if (breakSet)
                {
                    break;
                }
            }


Ok, kod jest prosty. Dajmy na to, że zamiast 2 pętli for mamy 4 pętle for.
int oneDimSize = 2;
int[, , ,] ints = new int[oneDimSize, 
oneDimSize,
oneDimSize,
oneDimSize];

bool breakSetForI = false;
for (int i = 0; i < oneDimSize; i++)
{
    bool breakSetForJ = false;
    for (int j = 0; j < oneDimSize; j++)
    {
        bool breakSetForK = false;
        for (int k = 0; k < oneDimSize; k++)
        {
            bool breakSetForL = false;
            for (int l = 0; l < oneDimSize; l++)
            {
                if (ints[i, j, k, l] == 0)
                {
                    breakSetForL = true;
                    break;
                }
            }
            if (breakSetForL)
            {
                breakSetForK = true;
                break;
            }
        }
        if (breakSetForK)
        {
            breakSetForJ = true;
            break;
        }

    }
    if (breakSetForJ)
    {
        breakSetForI = true;
        break;
    }
}

Tutaj już duplikujemy instrukcje if i musimy skorzystać z 4 zmiennych. To samo za pomocą goto wygląda następująco:
int oneDimSize = 2;
int[, , ,] ints = new int[oneDimSize,
 oneDimSize,
 oneDimSize,
 oneDimSize];

bool breakSet = false;
for (int i = 0; i < oneDimSize; i++)
{
    for (int j = 0; j < oneDimSize; j++)
    {
        for (int k = 0; k < oneDimSize; k++)
        {
            for (int l = 0; l < oneDimSize; l++)
            {
                if (ints[i, j, k, l] == 0)
                {
                    goto LabelIsZero;
                }
            }
        }
    }
}
return;

LabelIsZero:
breakSet = true;

Kod z zastosowaniem goto jest zdecydowanie czytelniejszy. Gdybyś miał jakiś inny pomysł na dobre zastosowanie goto to daj znać.