CodeGym /Kursy /C# SELF /Projekcja danych za pomocą

Projekcja danych za pomocą Select

C# SELF
Poziom 31 , Lekcja 3
Dostępny

1. Wprowadzenie

Kiedy oglądasz serial, nie zawsze poznajesz pełne informacje o wszystkich bohaterach, tylko to, co jest potrzebne do fabuły. W programowaniu często pojawiają się podobne sytuacje: nie potrzebujemy całego obiektu, tylko wybrane jego pola, wartości albo nawet jakieś obliczenia na ich podstawie.

Projekcja w LINQ — to przekształcenie elementów oryginalnej sekwencji do nowej formy. Zazwyczaj za pomocą metody Select. Możesz myśleć o Select jak o magicznej maszynie, która bierze każdy element, stosuje do niego funkcję i zwraca wynik. I tyle, żadnych sztuczek.

Kiedy potrzebna jest projekcja?

  • Chcemy wyświetlić tylko imiona użytkowników, a nie wszystkie dane o nich.
  • Przygotowujemy mailing: z obiektu klienta bierzemy tylko adres i imię.
  • Robimy obliczenie: do ceny produktu dodajemy podatek i dostajemy nową kolekcję.
  • Dla UI: trzeba pokazać tylko część danych, żeby nie przeładować interfejsu.

2. Metoda Select: składnia i podstawy

Podstawowa składnia

LINQ wspiera dwa style: metodyczny (Method Syntax) i zapytaniowy (Query Syntax). Dla Select w obu stylach wynik jest taki sam.

Składnia metodyczna

var query = myCollection.Select(x => x.CoZwrócić);

Tutaj x — zmienna dla każdego elementu kolekcji, a po prawej — wyrażenie, które zamienia element na to, co potrzebujesz.

Składnia zapytaniowa

var query = from x in myCollection
            select x.CoZwrócić;

Tutaj wszystko trochę bardziej przypomina SQL.

Prosty przykład: lista liczb

var numbers = new List<int> { 1, 2, 3, 4, 5 };
// Chcę dostać ich kwadraty
var squares = numbers.Select(n => n * n);

foreach (var s in squares)
{
    Console.WriteLine(s); // 1, 4, 9, 16, 25
}

Właśnie zamieniliśmy kolekcję liczb na kolekcję ich kwadratów — od razu, bez ręcznej pętli!

Przykład 2

W poprzednich przykładach mieliśmy już klasę Product:

public class Product
{
    public string Name { get; set; }
    public double Price { get; set; }
}

Załóżmy, że mamy listę:

var products = new List<Product>
{
    new Product { Name = "Czekolada", Price = 120 },
    new Product { Name = "Ser", Price = 250 },
    new Product { Name = "Chleb", Price = 55 }
};

Jeśli chcemy po prostu dostać nazwy wszystkich produktów, nie potrzebujemy całego obiektu, prawda? Potrzebujemy tylko listy nazw:

var names = products.Select(p => p.Name);
foreach (var name in names)
{
    Console.WriteLine(name); // Czekolada, Ser, Chleb
}

3. Zwracana kolekcja — jakie są opcje?

Zwróć uwagę, że Select zawsze zwraca kolekcję (dokładniej — IEnumerable<TOutput>), gdzie TOutput — typ wyniku funkcji. Sam decydujesz, co to będzie: string, liczba, typ anonimowy, nawet nowy obiekt.

Projekcja do nowego typu

Na przykład, chcesz dostać nie surowy obiekt, a jego "projekcję":

var projections = products.Select(p => new { p.Name, CenaZVAT = p.Price * 1.2 });

foreach (var item in projections)
{
    Console.WriteLine($"{item.Name}: {item.CenaZVAT}");
}

Tutaj stworzyliśmy nową kolekcję anonimowych obiektów — z nazwą i ceną z wyimaginowanym VAT-em!

4. Zwracamy nową klasę lub typ anonimowy

Możesz wybrać: tworzyć pełnoprawne klasy dla wyniku albo ograniczyć się do typów anonimowych, jeśli struktura jest potrzebna tylko "tu i teraz".

Typy anonimowe

var result = products.Select(p => new { Nazwa = p.Name, StaraCena = p.Price, NowaCena = p.Price + 40 });
foreach (var p in result)
{
    Console.WriteLine($"{p.Nazwa}: Było {p.StaraCena}, jest {p.NowaCena}");
}

Typy anonimowe są wygodne do szybkiego tworzenia wyników, szczególnie gdy nie ma sensu mnożyć klas dla jednej operacji.

Używamy własnej klasy do projekcji

Stwórzmy nową klasę:

public class ProductDto
{
    public string Name { get; set; }
    public double PriceWithTax { get; set; }
}

Teraz w środku zapytania LINQ:

var productsWithTax = products.Select(p => new ProductDto
{
    Name = p.Name,
    PriceWithTax = p.Price * 1.2
});

foreach (var p in productsWithTax)
{
    Console.WriteLine($"{p.Name}: {p.PriceWithTax}");
}

5. Dostęp do wewnętrznych kolekcji i właściwości

A co jeśli w środku obiektu jest kolekcja? Na przykład, każdy użytkownik ma listę zamówień.

public class User
{
    public string Name { get; set; }
    public List<Product> Purchases { get; set; }
}

Chcesz dostać listę wszystkich list zakupów? Żaden problem!

var allPurchases = users.Select(u => u.Purchases);

foreach (var purchaseList in allPurchases)
{
    foreach (var product in purchaseList)
    {
        Console.WriteLine(product.Name);
    }
}

Ważne! W tym przypadku mamy nie "płaską" listę produktów, a kolekcję kolekcji. Jak dostać JEDNĄ listę ze wszystkimi produktami wszystkich użytkowników? To będzie temat następnego wykładu — tam poznamy SelectMany.

6. Przydatne niuanse

Konwersja do typu kolekcji: ToList() i spółka

Select zwraca IEnumerable<T>. Jeśli chcesz zwykłą listę (List<T>), po prostu dodaj .ToList():

var nameList = products.Select(p => p.Name).ToList();

Teraz masz normalną listę, z którą możesz pracować jak zwykle.

Użycie indeksu elementu

Czasem przydaje się wiedzieć, który element z kolekcji jest teraz przetwarzany. Metoda Select ma przeciążenie:

var indexed = products.Select((product, index) => new { Index = index, Name = product.Name });
foreach (var item in indexed)
{
    Console.WriteLine($"{item.Index}: {item.Name}");
}

Tak można np. przygotować dane do ponumerowanych list.

Projekcja w programowaniu

Znajomość Select — to częste pytanie na rozmowach kwalifikacyjnych. Bez tej konstrukcji trudno sobie wyobrazić nowoczesne aplikacje:

  • Pobieranie tylko potrzebnych danych z bazy lub zewnętrznych serwisów.
  • Przygotowanie danych do przekazania między warstwami aplikacji.
  • Przekształcanie danych do formatu, który nadaje się do wyświetlenia na ekranie lub wysłania przez sieć.
  • Oszczędność pamięci (nie ciągniemy całego obiektu kolekcji, tylko potrzebne pola).

Firmy pracujące z dużymi ilościami danych często budują na tym całą logikę biznesową.

7. Typowe błędy i cechy szczególne

Czasem początkujący programiści trafiają na irytujące bugi — zwykle przez niezrozumienie, jak działa leniwe wykonanie (deferred execution) w LINQ. Na przykład, po wywołaniu Select nie zawsze następuje natychmiastowe wykonanie zapytania — tylko gdy naprawdę iterujesz po kolekcji (foreach, ToList() itd.). To pozwala budować "pipeline przetwarzania", ale czasem prowadzi do tego, że wynik się zmienia, jeśli oryginalne dane zostały zmienione przed użyciem wyniku zapytania.

Pamiętaj też, że Select nie zmienia oryginalnej kolekcji — buduje nową. Więc jeśli nadal spodziewasz się znaleźć w products nowe pola po products.Select(...), to się nie wydarzy.

Obsługa null: jeśli projekcja zakłada dostęp do potencjalnie null pól, nie zapomnij o tym zadbać. W C# 8+ pojawia się ostrzeżenie kompilatora, jeśli można dostać NullReferenceException.

2
Zadanie
C# SELF, poziom 31, lekcja 3
Niedostępne
Projekcja obiektów z obliczeniem nowej wartości
Projekcja obiektów z obliczeniem nowej wartości
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION