xion.log » LINQ

Posts tagged ‘LINQ’

Metody rozszerzające w C#

2009-09-11 12:28

Wspomnę dzisiaj o dość dziwnej funkcji, która została dodana w wersji 3.0 języka C#. Polega ona na możliwości dodania nowych metod do istniejących klas bez zmiany ich definicji. Odbywa się to poprzez zdefiniowanie tych dodatkowych metod jako statycznych (w innych klasach) i użyciu specjalnej składni dla jej pierwszego parametru. Oto przykład:

  1. public static class StringExtensions
  2. {
  3.     // zwraca liczbę znaków w ciągu
  4.     public static int WordCount(this String str)
  5.         { return str.Split(new char[] { ' ' }).Length; }
  6. }

Jeśli ktoś co nieco wie o tym, jak od środka działają metody obiektów w C++, to pewnie przypomina sobie, że to co wewnątrz metody jest wskaźnikiem this w rzeczywistości przekazywane jest metodzie jako jej pierwszy parametr. Pewnie ten fakt posłużył twórcom C# jako inspiracja przy ustalaniu składni metod rozszerzających.
Jak to działa? Otóż bardzo prosto. Dzięki powyższej definicji standardowa klasa System.String została teraz wzbogacona o nową metodę WordCount, której możemy użyć tak samo, jak każdej innej metody tej klasy:

  1. string s = "Ala ma kota";
  2. int words = s.WordCount();

Powód, dla którego fakt istnienia podobnego feature‘a nazwałem dość dziwnym, powinien się w tej chwili stać nieco jaśniejszy. Po krótkim zastanowieniu można bowiem stwierdzić, że niemal identyczny efekt można osiągnąć dwoma innymi sposobami:

  • Po pierwsze, możemy zdefiniować klasę pochodną po tej, której chcemy dodać metodę. Albo wręcz zdefiniować sobie po prostu zwykłą funkcję przyjmującą jeszcze zwyklejszy parametr i mieć coś w stylu StringUtils.WordCount(s). Jak widać, co najmniej jeden z tych sposobów można zastosować do każdej klasy – także takiej, której definicją nie dysponujemy (co w gruncie rzeczy jest banalne; na takich “sztuczkach” polega przecież całe programowanie obiektowe ;-]).
  • Po drugie, możemy zwyczajne pójść do deklaracji klasy i żądaną metodę dodać. Naturalnie, musi to być nasza własna klasa, której kodem dysponujemy i który możemy zmieniać.

Cechą wspólną obu tych “obejść” jest to, iż to my chcemy mieć tę nową metodę i to my ją definiujemy – zapewne dlatego, że to my chcemy jej potem używać. A skoro tak, to mamy wolność w ustaleniu sposobu jej wywoływania, ergo: nie ma znaczenia, jak ją zdefiniujemy. Któryś z tych dwóch/trzech sposobów będzie więc wystarczający.

Czyżby więc wychodziło na to, że C# w końcu dorobił się feature‘a, który rzeczywiście niczemu nie służy i jest kompletnie bez sensu? :) Prawie… Jak to z większością nowości w C# 3.0, przyczyna wprowadzenia metod rozszerzających streszcza się w – za przeproszeniem – czterech literach: LINQ.
LINQ opiera się bowiem na rozszerzeniu interfejsów pojemników (np. IEnumerable) o dodatkowe metody służące wykonywaniu na nich quasi-SQL-owych zapytań – jak np. Select czy Where. One właśnie zostały zaimplementowane jako metody rozszerzające. A że ponadto trafiły one do przestrzeni nazw System.Linq, to stają się dostępne dopiero po zadeklarowaniu użycia tej właśnie przestrzeni.
Reasumując: twórcy .NET-a dodali dwie nowości (metody rozszerzające oraz LINQ) po to, żeby ta pierwsza pozwalała na nieużywanie tej drugiej. Genialne, czyż nie? ;D

Tags: ,
Author: Xion, posted under Programming » 6 comments

LINQ bywa przydatny

2009-07-09 19:35

Wśród feature‘ów wprowadzonych w wersji 3.5 frameworka .NET jest między innymi LINQ (Language INtegrated Query). Mechanizm ten umożliwia – w dużym skrócie rzecz jasna – konstruowanie zapytań odnoszących się do kolekcji obiektów (w zasadzie dowolnego rodzaju) przy pomocy operatorów znanych z relacyjnych baz danych, jak SELECT czy WHERE. Ponadto w .NET 3.5 język C# został też odpowiednio rozszerzony, aby zapytania ta składniowo mogły przypominać kwerendy podobne do tych występujących w różnych odmianach języka SQL.

“A po co to komu?”, można zapytać od razu i nie ukrywam, że moja reakcja na LINQ była podobna. W końcu kto by chciał zaśmiecać taki ładny język jak C# zupełnie nieprzydatnym nikomu SQL-em? ;] Poza tym mówimy tutaj o czymś trochę z innej bajki, bowiem języki zapytań różnią się od języków programowania tym, że są deklaratywne: pisanie w nich polega na określeniu, co chcemy otrzymać – nie zaś tego, co po kolei program ma robić. (Jak nietrudno się domyślić, ogranicza to trochę stosowalność takich języków). Czemu więc chce się te dwie rzeczy połączyć?…

Odpowiedź jest jak zwykle dość prosta: dla wygody. Za pomocą LINQ nie zrobi się niczego nowego, ale pewne operacje można wykonywać prościej. Czasami można sobie oszczędzić dodatkowych pętli i zmiennych lokalnych – jak chociażby w poniższym przykładzie, w którym chcemy pobrać wszystkie wartości zaznaczone na liście z polami wyboru (check box list) i coś z nimi zrobić:

  1. // wersja z pętlą
  2. List<int> selected = new List<int>();
  3. foreach (ListItem item in checkBoxList.Items)
  4.     if (item.Selected) selected.Add (Convert.ToInt32(item.Value));
  5. DoSomething (selected);
  6.  
  7. // wersja LINQ
  8. DoSomething (from ListItem item in checkBoxList.Items
  9.              where item.Selected
  10.              select Convert.ToInt32(item.Value));

Można się oczywiście spierać, która wersja jest czytelniejsza i którą jest łatwiej/szybciej napisać. To jednak jest w dużym stopniu kwestią subiektywną. Jak dla mnie możliwość uniknięcia kolejnej pętli, która udaje, że coś robi (a tak naprawdę tylko przerzuca dane z jednego miejsca w inne) jest całkowicie zadowalająca.
Przy bardziej skomplikowanych przypadkach miałbym aczkolwiek dużo większe możliwości. Jest oczywiście możliwe pisanie błyskotliwych kwerend przemiatających po cztery pojemniki naraz i używających takich operatorów jak group czy orderby, by zrobić w trzech linijkach zapytania to, co inaczej wymagałoby 20 wierszy kodu. Sęk w tym, że prawdopodobnie odczytanie znaczenia tych 20 wierszy byłoby wówczas znacząco łatwiejsze ;P

Tags: ,
Author: Xion, posted under Programming » 10 comments
 


© 2018 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.