Coraz częściej w projektach zaczynam używać LINQ i zaczynam dostrzegać zalety. Nie dość, że można w ten sposób trochę pisania kodu oraz zyskać na przejrzystości. Poniżej zamieszczę parę przykładów, w których pomału przerzucam się na LINQ. W celach demonstracyjnych stwórzmy klasę Person, której obiekty będą reprezentowały osoby.
class Person : IComparable { public Person(string name, string surname, int age, bool married) { this.Name = name; this.Surname = surname; this.Age = age; this.Married = married; } public Person() {} public string Name { get; set; } public string Surname { get; set; } public int Age { get; set; } public bool Married { get; set; } public int CompareTo(object obj) { Person otherPerson = obj as Person; if (otherPerson != null) return Surname.CompareTo(otherPerson.Surname) == 0 ? (Name.CompareTo(otherPerson.Name) == 0 ? Age.CompareTo(otherPerson.Age) : Name.CompareTo(otherPerson.Name)) : Surname.CompareTo(otherPerson.Surname); else throw new ArgumentException("Object is not a Person"); } }
Jak widać klasa ma cztery własności:
- imię,
- nazwisko,
- wiek,
- informację, czy ktoś ma żonę lub męża.
Dodatkowo w klasie zostały zdefiniowane dwa konstruktory. Jeden domyślny, a drugi przypisujący wartości do wcześniej wspomnianych własności. Klasa również implementuje interfejs IComparable, ale o tym później.
Pierwszy scenariusz, w którym chcę zademonstrować wyższość LINQ nad klasycznym podejściem jest sytuacja w której należy wybrać obiekty spełniające określone kryteria. Dla przykładu załóżmy, że należy wybrać obiekty, które spełniają następujące warunki:
- osoby muszą mieć więcej niż 20 lat,
- osoby są żonate, bądź też zamężne.
Standardowo takie problem rozwiązuje się budując pętlę, która przechodzi przez wszystkie elementy i sprawdza, czy dany obiekt spełnia zadane kryteria. W sytuacji, gdy kryteria zostają spełnione dany obiekt kopiuje się do docelowej listy. Powyższy algorytm prezentuje poniższy kod:
List<Person> filtered = new List<Person>(); foreach (Person pearson in people) { if (pearson.Age > 20 && pearson.Married) { filtered.Add(pearson); } }
To samo można zrobić teoretycznie za pomocą jednej linijki kodu przy pomocy LINQ:
var filtered2 = from currentPearson in people where currentPearson.Age > 20 && currentPearson.Married select currentPearson;
Wydaje mi się, że rozwiązanie za pomocą LINQ jest o wiele bardziej czytelniejsze i łatwiejsze do późniejszego utrzymania.
Kolejny przykład jest modyfikacją obecnego. Dodajmy do przykładu, że chcemy pobrać tylko dwa pierwsze elementy. W klasycznym podejściu można zrobić to w następujący sposób:
List<Person> filtered = new List<Person>(); for (int i = 0; i < people.Count && filtered.Count < 2; i++) { if (people[i].Age > 20 && people[i].Married) { filtered.Add(people[i]); } }
W LINQ kod ten można zastąpić w następujący sposób:
var filtered2 = people.Where(p => p.Age > 20 && p.Married).Take(2);
I ponownie można zauważyć, że rozwiązanie LINQ jest bardziej proste i czytelniejsze.
W moim kodzie dość często pojawia się również potrzeba pobrania pierwszego elementu z kolekcji. W klasyczny sposób należy sprawdzić czy kolekcja została poprawnie zainicjalizowana oraz czy zawiera jakieś elementy. Dopiero potem można pobrać interesujący nas element.
Person person1; if (people != null && people.Count > 0) { person1 = people[0]; }
W LINQ zadanie to jest banalne:
Person person2 = people.FirstOrDefault();
Ostatni przykład związany jest z sortowaniem. Klasycznie, aby posortować kolekcję należy w obiekcie zaimplementować interfejs IComparable i użyć metody Sort(). Implementację tego interfejsu można znaleźć na początku artykułu w miejscu gdzie przedstawiona klasa Person. Rozwiązanie jest stosunkowo proste, jeśli chce się posortować listę zawsze w ten sam sposób. Ale już w sytuacji, kiedy chcemy, aby była możliwość sortowania na różne sposoby sprawa się już trochę komplikuje.
people.Sort();
Sprawa wygląda o wiele bardziej optymistycznie w LINQ. Wystarczy tylko jedna linijka, aby osiągnąć to co dała nam implementacja interfejsu IComparable:
var sortedPeople = people.OrderBy(u => u.Surname).ThenBy(u => u.Name).ThenBy(u => u.Age);
Dodatkowo, jeśli chcemy w następnej linijce chcemy zmienić kolejność sortowania to nic nie stoi, ku temu na przeszkodzie.
sortedPeople = people.OrderBy(u => u.Surname).ThenBy(u => u.Name).ThenByDescending(u => u.Age);
Jak również nie ma przeszkód, aby przed sortowanie zawęzić wyniki i wybrać tylko te elementy, które nas interesują:
sortedPeople = people.Where(p => p.Age > 20 && p.Married).OrderBy(u => u.Surname) .ThenBy(u => u.Name).ThenByDescending(u => u.Age);
Cześć,
potrzebuję dopasować zapytanie w linq do wzorca regularnego mam coś takiego ale mi to nie działa…
Regex rgx = new Regex(pattern);
string passtru = db.Users.Where(s => s.login == log && s.password.rgx.IsMatch(pattern)).FirstOrDefault().name;
jakaś podpowiedź?
Łap przykład:
var names = new string[] { „Michal”, „Adam”};
string pattern = „M*”;
Regex rgx = new Regex(pattern);
var nameOnM = names.Where(name => rgx.IsMatch(name)).FirstOrDefault();
Mimo że lubię LINQ, uważam że należy używać go z głową i rozumieć jak działa.
Ja w firmie znajduję np. takie kwiatki:
private int Get(IList lista)
{
var filteredItems = lista.Where(item => CanSomething(item));
var value = filteredItems.Count() > 0 ? filteredItems.First().Value : 0;
return paymentValue;
}
A można przecież napisać też czytelnie i wydajniej (bez LINQ) :
private int Get(IList lista)
{
for (var i = 0; i < lista.Count; i++)
{
if (CanSomething(lista[i]))
return lista[i].Value;
}
return 0;
}