Wykorzystanie wyrażeń lambda w naszych klasach

Wyrażenia lambda jest to nowa konstrukcja wprowadzona do C# 3.0 pozwalająca w łatwy i przejrzysty sposób na "szybko" zdefiniować jakąś funkcję. W tej notce napiszę jak można je użyć do definiowan

Nadrzędnym typem każdego wyrażenia lambda jest typ Func<T1, T2, T3, T4, TResult> gdzie T1-T4 są typami argumentów, a TResult typem wyniku. Nie jesteśmy oczywiście przywiązani do czterech parametrów wejściowych, gdyż zostały zdefiniowane typy od 0 do 4 argumentowe (zmienia się liczba wystąpień TX). TResult jest natomiast parametrem obowiązkowym.

W poniższym przykładzie zdefiniuję metodę w klasie Matrix, która jako argument będzie otrzymywała wyrażenie lambda (funkcję) i wykona ją dla każdego z elementów macierzy. Przechodząc do praktyki będzie to wyglądało tak:

  1. public void DoForAllElements(Func< T , T> function)
  2. {
  3.  
  4. for (int i = 0; i < N; i++)
  5. {
  6. for (int j = 0; j < M; j++)
  7. {
  8. this[i, j] = function(this[i, j]);
  9. }
  10. }
  11. }
  12.  

Wykorzystać ją możemy w sposób następujący:

  1. var A = new Matrix< Double , DoubleCalculator>();
  2. A.DoForAllElements( x => Math.Sqrt(x) );
  3.  

Prawda, że proste? :)

Typy generyczne w C# i „drobne” niedociągnięcie

Zacznę może od tego, że potrzebuję w pewnym projekcie pisanym w C# obsługę macierzy (dodawanie, mnożenie, ślad, itp). Konkretniej chodzi o macierz skonstruowaną z elementów typu prostego Double, ale dobrą cechą byłaby jej uniwersalność (czyli obsługa dowolnych typów) – w skrócie macierz generyczna. Ponieważ jestem troszku leniwy więc rozpocząłem poszukiwania odpowiednich bibliotek w Internecie. Niestety jedynym rozwiązaniem godnym przejrzenia, aczkolwiek niespełniącym moich wymagań była biblioteka CSML. Wadą jej polega na tym, że natywnie operuje na liczbach zespolonych (co można oczywiście zarzutować w przestrzeń liczb rzeczywistch) i nie ma domyślnie zrobionej konwersji Double na Complex.

Zdziwiło mnie, że taka ważna struktura w obliczeniach numerycznych nie dorobiła się pożądnej implementacji (dodam jeszcze: darmowej).. No cóż, w takim razie należy samemu napisać odpowiedni kod.

Zacząłem od stworzenia szkieletu klasy:

  1. public class Matrix< T >
  2. {
  3. T[,] matrix;
  4. public int N { get; private set; }
  5. public int M { get; private set; }
  6. public Matrix(int n, int m)
  7. {
  8. }
  9.  
  10. public T this[int i, int j]
  11. {
  12. }
  13. public static Matrix< T > operator +(Matrix< T > A, Matrix< T > B)
  14. {
  15. }
  16. public static Matrix< T > operator -(Matrix< T > A, Matrix< T > B)
  17. {
  18. }
  19. public static Matrix< T > operator *(Matrix< T > A, Matrix< T > B)
  20. {
  21. }
  22. }
  23.  

 

Dokonałem następnie implementacji pierwszego z operatorów:

  1. public static Matrix< T > operator +(Matrix< T > A, Matrix< T > B)
  2. {
  3. if (A.N == B.N && A.M == B.M)
  4. {
  5. Matrix< T > result = new Matrix< T >(A.N, A.M);
  6. for (int i = 0; i < A.N; i++)
  7. {
  8. for (int j = 0; j < A.M; j++)
  9. {
  10. result[i, j] = A[i, j] + B[i, j];
  11. }
  12. }
  13. return result;
  14. }
  15. else
  16. {
  17. throw new Exception("Matrices have different dimensions");
  18. }
  19. }

Próba kompilacji i... błąd: "Operator '+' cannot be applied to operands of type 'T' and 'T'". W sumie prawda, skąd kompilator ma wiedzieć, czy typ T ma zaimplementowany operator dodawania. Właśnie po to wprowadzono system reguł mogący wymusić na danym typie kilka cech. Zaglądam więc do dokumentacji MSDN i niestety nic ciekawego nie znalazłem. Odpalam wyszukiwarkę dotarłem do kilku wątków o tym samym problemie i jedyną odpowiedzią jest to, że: nie można czegoś takiego zrobić ponieważ Double jest typem prostym.

Ostatecznie jest jeszcze jedno rozwiązanie ("Using generics for calculations"), ale moim zdaniem nie do końca jest ono wzorowe z tego względu, że należałoby stosować dodatkową warstwę pomiędzy typami Int32, Double a naszą klasą generyczną. Ale dużego wyboru nie ma..

Microsoft SQL Server 2008 CTP6 (February 2008)

Dwa dni temu na stronach Microsoftu można było zauważyć długo oczekiwane następne wydanie CTP SQL Servera 2008, które w przeciwieństwie do CTP5 zaczęło oficjalnie wspierać LINQ to SQL w Visual Studio 2008. Pierwszy kontakt z CTP6 wyszedł dosyć dobrze, bez problemów dało zainstalować się na kilku serwerach (opartych na Windows Server 2008) – nie pojawił się żaden błąd i wszystko działało od ręki. Problemy zaczęły się dopiero w momencie, gdy zapragnąłem zainstalować go na moim laptopie. A zaczęło się to mniej więcej tak ;)

Odinstalowałem (tzn. spróbowałem odinstalować) wpierw poprzednią wersję SQL Server 2008 oraz SQL Server Express 2005, niestety ten proces nie do końca się powiódł, gdyż wystąpił błąd o niezbyt rozbudowanej treści "Setup has encountered the following error: Failed to retrieve data for this request..". W Internecie jedyne co udało mi się znaleźć to opis, że trzeba ręcznie usunąć aplikacje (np. za pomocą Windows Install Clean Up, a następnie wyczyścić pliki z katalogu Program Files). Tak więc też uczyniłem.

Po restarcie systemu i odpaleniu instalatora SQL Server 2008 w niecałe 30s pojawił się znowu identyczny błąd (gdy odpaliłem ręcznie instalator SQL Database Engine wyświetlił jakieś dziwne zapytanie SQL i również instalacja się nie powiodła). Google już niestety na ten temat milczało. Ale ponieważ tak łatwo nie można się poddać, więc szukałem dalej;).

Rozwiązanie problemu znajdowało się w sercu systemu Windows – w rejestrze :) Postanowiłem być trochę brutalny i usunąłem wszystkie wpisy związane z SQL Serverem z klucza HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ (delikatne próby usuwania wybiórczych wpisów także spełzły na niczym). Efektem tego było szczęśliwe zainstalowanie Microsoft SQL Server 2008 CTP6. Można więc wracać już do zabawy :)

Download: Microsoft SQL Server 2008 CTP6 (February 2008)

LINQ to XSD

Dzisiaj wyszła kolejna wersja Alpha 0.2 LINQ to XSD. Tym razem już działająca z Visual Studio 2008. Niestety chwilowo nie mam czasu, aby dokończyć moją kostkę, ale wiem już przynajmniej jakiego rozwiązania wybiorę :)

Download: LINQ to XSD Alpha 0.2