C# Language
Zapytania LINQ
Szukaj…
Wprowadzenie
LINQ jest akronimem oznaczającym L anguage IN tegrated Q aery. Jest to koncepcja, która integruje język zapytań, oferując spójny model pracy z danymi w różnych źródłach i formatach danych; używasz tych samych podstawowych wzorców kodowania, aby wyszukiwać i przekształcać dane w dokumentach XML, bazach danych SQL, zestawach danych ADO.NET, kolekcjach .NET i dowolnym innym formacie, dla którego dostępny jest dostawca LINQ.
Składnia
Składnia zapytania:
- z <zmiennej zakresu> w <kolekcji>
- [z <zmiennej zakresu> w <kolekcji>, ...]
- <filtr, łączenie, grupowanie, agregowanie operatorów, ...> <wyrażenie lambda>
- <wybierz lub zgrupuj według operatora> <sformułuj wynik>
Składnia metody:
- Enumerable.Aggregate (func)
- Enumerable.Aggregate (seed, func)
- Enumerable.Aggregate (seed, func, resultSelector)
- Enumerable.All (predicate)
- Enumerable.Any ()
- Enumerable.Any (predicate)
- Enumerable.AsEnumerable ()
- Enumerable.Average ()
- Wyliczalny Średnia (selektor)
- Enumerable.Cast <Result> ()
- Enumerable.Concat (drugi)
- Enumerable.Contains (wartość)
- Enumerable.Contains (wartość, moduł porównujący)
- Enumerable.Count ()
- Enumerable.Count (predicate)
- Enumerable.DefaultIfEmpty ()
- Enumerable.DefaultIfEmpty (defaultValue)
- Enumerable.Distinct ()
- Enumerable.Distinct (porównaj)
- Enumerable.ElementAt (indeks)
- Enumerable.ElementAtOrDefault (indeks)
- Enumerable.Empty ()
- Enumerable.Except (second)
- Enumerable.Except (drugi, moduł porównujący)
- Enumerable.First ()
- Enumerable.First (predicate)
- Enumerable.FirstOrDefault ()
- Enumerable.FirstOrDefault (predykat)
- Enumerable.GroupBy (keySelector)
- Enumerable.GroupBy (keySelector, resultSelector)
- Enumerable.GroupBy (keySelector, elementSelector)
- Enumerable.GroupBy (keySelector, porównywarka)
- Enumerable.GroupBy (keySelector, resultSelector, porównywarka)
- Enumerable.GroupBy (keySelector, elementSelector, resultSelector)
- Enumerable.GroupBy (keySelector, elementSelector, porównywarka)
- Enumerable.GroupBy (keySelector, elementSelector, resultSelector, porównywarka)
- Enumerable.Intersect (drugi)
- Enumerable.Intersect (drugi, moduł porównujący)
- Enumerable.Join (inner, outerKeySelector, innerKeySelector, resultSelector)
- Enumerable.Join (inner, outerKeySelector, innerKeySelector, resultSelector, porównywarka)
- Enumerable.Last ()
- Enumerable.Last (predicate)
- Enumerable.LastOrDefault ()
- Enumerable.LastOrDefault (predykat)
- Enumerable.LongCount ()
- Enumerable.LongCount (predicate)
- Enumerable.Max ()
- Enumerable.Max (selektor)
- Enumerable.Min ()
- Enumerable.Min (selektor)
- Enumerable.OfType <TResult> ()
- Enumerable.OrderBy (keySelector)
- Enumerable.OrderBy (keySelector, porównywarka)
- Enumerable.OrderByDescending (keySelector)
- Enumerable.OrderByDescending (keySelector, porównywarka)
- Enumerable.Range (start, count)
- Enumerable.Repeat (element, count)
- Enumerable.Reverse ()
- Enumerable.Select (selektor)
- Enumerable.SelectMany (selektor)
- Enumerable.SelectMany (collectionSelector, resultSelector)
- Enumerable.SequenceEqual (drugi)
- Enumerable.SequenceEqual (drugi, moduł porównujący)
- Enumerable.Single ()
- Enumerable.Single (predicate)
- Enumerable.SingleOrDefault ()
- Enumerable.SingleOrDefault (predykat)
- Enumerable.Skip (count)
- Enumerable.SkipWhile (predicate)
- Enumerable.Sum ()
- Enumerable.Sum (selektor)
- Enumerable.Take (count)
- Enumerable.TakeWhile (predicate)
- orderEnumerable.ThenBy (keySelector)
- orderEnumerable.ThenBy (keySelector, porównywarka)
- orderEnumerable.ThenByDescending (keySelector)
- orderEnumerable.ThenByDescending (keySelector, porównywarka)
- Enumerable.ToArray ()
- Enumerable.ToDictionary (keySelector)
- Enumerable.ToDictionary (keySelector, elementSelector)
- Enumerable.ToDictionary (keySelector, porównywarka)
- Enumerable.ToDictionary (keySelector, elementSelector, porównywarka)
- Enumerable.ToList ()
- Enumerable.ToLookup (keySelector)
- Enumerable.ToLookup (keySelector, elementSelector)
- Enumerable.ToLookup (keySelector, porównywarka)
- Enumerable.ToLookup (keySelector, elementSelector, porównywarka)
- Enumerable.Union (drugi)
- Enumerable.Union (drugi, moduł porównujący)
- Enumerable.Where (predicate)
- Enumerable.Zip (second, resultSelector)
Uwagi
Aby korzystać z zapytań LINQ, musisz zaimportować System.Linq
.
Składnia metody jest bardziej wydajna i elastyczna, ale składnia zapytania może być prostsza i bardziej znana. Wszystkie zapytania napisane w składni zapytania są tłumaczone przez kompilator na składnię funkcjonalną, więc wydajność jest taka sama.
Obiekty zapytania nie są oceniane, dopóki nie zostaną użyte, więc można je zmienić lub dodać bez obniżenia wydajności.
Gdzie
Zwraca podzbiór elementów, dla których określony predykat jest dla nich prawdziwy.
List<string> trees = new List<string>{ "Oak", "Birch", "Beech", "Elm", "Hazel", "Maple" };
Składnia metody
// Select all trees with name of length 3
var shortTrees = trees.Where(tree => tree.Length == 3); // Oak, Elm
Składnia zapytania
var shortTrees = from tree in trees
where tree.Length == 3
select tree; // Oak, Elm
Wybierz - Przekształcanie elementów
Wybierz pozwala zastosować transformację do każdego elementu w dowolnej strukturze danych implementującej IEnumerable.
Uzyskiwanie pierwszego znaku każdego ciągu z poniższej listy:
List<String> trees = new List<String>{ "Oak", "Birch", "Beech", "Elm", "Hazel", "Maple" };
Używanie regularnej składni (lambda)
//The below select stament transforms each element in tree into its first character.
IEnumerable<String> initials = trees.Select(tree => tree.Substring(0, 1));
foreach (String initial in initials) {
System.Console.WriteLine(initial);
}
Wynik:
O
b
b
mi
H.
M.
Wersja demonstracyjna na żywo .NET Fiddle
Korzystanie ze składni zapytania LINQ
initials = from tree in trees
select tree.Substring(0, 1);
Metody łączenia
Wiele funkcji LINQ działa zarówno na IEnumerable<TSource>
i zwraca IEnumerable<TResult>
. Parametry typu TSource
i TResult
mogą, ale nie muszą odnosić się do tego samego typu, w zależności od metody i przekazanych do niej funkcji.
Oto kilka przykładów
public static IEnumerable<TResult> Select<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TResult> selector
)
public static IEnumerable<TSource> Where<TSource>(
this IEnumerable<TSource> source,
Func<TSource, int, bool> predicate
)
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector
)
Podczas gdy niektóre metody łączenia łańcuchów mogą wymagać przetworzenia całego zestawu przed przejściem dalej, LINQ korzysta z odroczonego wykonania przy użyciu MSDN zwrotu, który tworzy Enumerable i Enumerator za kulisami. Proces tworzenia łańcuchów w LINQ polega zasadniczo na zbudowaniu elementu wymiennego (iteratora) dla oryginalnego zestawu - który jest odroczony - dopóki nie zostanie zmaterializowany przez wyliczenie elementu wymiennego .
Pozwala to na płynne łączenie tych funkcji w wiki , gdzie jedna funkcja może działać bezpośrednio na skutek innej. Tego stylu kodu można użyć do wykonania wielu operacji opartych na sekwencji w jednej instrukcji.
Na przykład możliwe jest połączenie Select
, Where
i OrderBy
celu przekształcenia, filtrowania i sortowania sekwencji w pojedynczej instrukcji.
var someNumbers = { 4, 3, 2, 1 };
var processed = someNumbers
.Select(n => n * 2) // Multiply each number by 2
.Where(n => n != 6) // Keep all the results, except for 6
.OrderBy(n => n); // Sort in ascending order
Wynik:
2)
4
8
Wersja demonstracyjna na żywo .NET Fiddle
Wszelkie funkcje, które zarówno rozszerzają, jak i zwracają ogólny typ IEnumerable<T>
mogą być używane jako klauzule łańcuchowe w pojedynczej instrukcji. Ten styl płynnego programowania jest potężny i należy go wziąć pod uwagę przy tworzeniu własnych metod rozszerzenia .
Zasięg i powtórzenie
Do generowania prostych sekwencji można użyć metod statycznych Range
and Repeat
w Enumerable
.
Zasięg
Enumerable.Range()
generuje sekwencję liczb całkowitych o podanej wartości początkowej i liczbie.
// Generate a collection containing the numbers 1-100 ([1, 2, 3, ..., 98, 99, 100])
var range = Enumerable.Range(1,100);
Wersja demonstracyjna na żywo .NET Fiddle
Powtarzać
Enumerable.Repeat()
generuje sekwencję powtarzających się elementów na podstawie elementu i wymaganej liczby powtórzeń.
// Generate a collection containing "a", three times (["a","a","a"])
var repeatedValues = Enumerable.Repeat("a", 3);
Wersja demonstracyjna na żywo .NET Fiddle
Skip and Take
Metoda Skip zwraca kolekcję wykluczającą liczbę elementów z początku kolekcji źródłowej. Liczba wykluczonych pozycji to liczba podana jako argument. Jeśli w kolekcji jest mniej elementów niż podano w argumencie, zwracana jest pusta kolekcja.
Metoda Take zwraca kolekcję zawierającą wiele elementów z początku kolekcji źródłowej. Liczba uwzględnionych elementów to liczba podana jako argument. Jeśli w kolekcji jest mniej elementów niż podano w argumencie, zwrócona kolekcja będzie zawierać te same elementy, co kolekcja źródłowa.
var values = new [] { 5, 4, 3, 2, 1 };
var skipTwo = values.Skip(2); // { 3, 2, 1 }
var takeThree = values.Take(3); // { 5, 4, 3 }
var skipOneTakeTwo = values.Skip(1).Take(2); // { 4, 3 }
var takeZero = values.Take(0); // An IEnumerable<int> with 0 items
Wersja demonstracyjna na żywo .NET Fiddle
Pomiń i zabierz są powszechnie używane razem do dzielenia wyników, na przykład:
IEnumerable<T> GetPage<T>(IEnumerable<T> collection, int pageNumber, int resultsPerPage) {
int startIndex = (pageNumber - 1) * resultsPerPage;
return collection.Skip(startIndex).Take(resultsPerPage);
}
Ostrzeżenie: LINQ to Entities obsługuje tylko Skip w zamówionych zapytaniach . Jeśli spróbujesz użyć funkcji Skip bez zamówienia, otrzymasz NotSupportedException z komunikatem „Metoda Skip” jest obsługiwana tylko dla posortowanych danych wejściowych w LINQ do encji. Metodę „OrderBy” należy wywołać przed metodą „Skip”. ”
Najpierw FirstOrDefault, Last, LastOrDefault, Single i SingleOrDefault
Wszystkie sześć metod zwraca pojedynczą wartość typu sekwencji i można je wywoływać z predykatem lub bez niego.
W zależności od liczby elementów pasujących do predicate
lub, jeśli nie predicate
, liczby elementów w sekwencji źródłowej, zachowują się one w następujący sposób:
Pierwszy()
- Zwraca pierwszy element sekwencji lub pierwszy element pasujący do podanego
predicate
. - Jeśli sekwencja nie zawiera elementów,
InvalidOperationException
jestInvalidOperationException
z komunikatem: „Sekwencja nie zawiera elementów”. - Jeśli sekwencja nie zawiera elementów pasujących do podanego
predicate
,InvalidOperationException
jestInvalidOperationException
z komunikatem „Sekwencja nie zawiera pasującego elementu”.
Przykład
// Returns "a":
new[] { "a" }.First();
// Returns "a":
new[] { "a", "b" }.First();
// Returns "b":
new[] { "a", "b" }.First(x => x.Equals("b"));
// Returns "ba":
new[] { "ba", "be" }.First(x => x.Contains("b"));
// Throws InvalidOperationException:
new[] { "ca", "ce" }.First(x => x.Contains("b"));
// Throws InvalidOperationException:
new string[0].First();
Wersja demonstracyjna na żywo .NET Fiddle
FirstOrDefault ()
- Zwraca pierwszy element sekwencji lub pierwszy element pasujący do podanego
predicate
. - Jeśli sekwencja nie zawiera elementów lub nie pasuje do podanego
predicate
, zwraca domyślną wartość typu sekwencji przy użyciudefault(T)
.
Przykład
// Returns "a":
new[] { "a" }.FirstOrDefault();
// Returns "a":
new[] { "a", "b" }.FirstOrDefault();
// Returns "b":
new[] { "a", "b" }.FirstOrDefault(x => x.Equals("b"));
// Returns "ba":
new[] { "ba", "be" }.FirstOrDefault(x => x.Contains("b"));
// Returns null:
new[] { "ca", "ce" }.FirstOrDefault(x => x.Contains("b"));
// Returns null:
new string[0].FirstOrDefault();
Wersja demonstracyjna na żywo .NET Fiddle
Ostatni, ubiegły, zeszły()
- Zwraca ostatni element sekwencji lub ostatni element pasujący do podanego
predicate
. - Jeśli sekwencja nie zawiera elementów,
InvalidOperationException
jestInvalidOperationException
z komunikatem „Sekwencja nie zawiera elementów”. - Jeśli sekwencja nie zawiera elementów pasujących do podanego
predicate
,InvalidOperationException
jestInvalidOperationException
z komunikatem „Sekwencja nie zawiera pasującego elementu”.
Przykład
// Returns "a":
new[] { "a" }.Last();
// Returns "b":
new[] { "a", "b" }.Last();
// Returns "a":
new[] { "a", "b" }.Last(x => x.Equals("a"));
// Returns "be":
new[] { "ba", "be" }.Last(x => x.Contains("b"));
// Throws InvalidOperationException:
new[] { "ca", "ce" }.Last(x => x.Contains("b"));
// Throws InvalidOperationException:
new string[0].Last();
LastOrDefault ()
- Zwraca ostatni element sekwencji lub ostatni element pasujący do podanego
predicate
. - Jeśli sekwencja nie zawiera elementów lub nie pasuje do podanego
predicate
, zwraca domyślną wartość typu sekwencji przy użyciudefault(T)
.
Przykład
// Returns "a":
new[] { "a" }.LastOrDefault();
// Returns "b":
new[] { "a", "b" }.LastOrDefault();
// Returns "a":
new[] { "a", "b" }.LastOrDefault(x => x.Equals("a"));
// Returns "be":
new[] { "ba", "be" }.LastOrDefault(x => x.Contains("b"));
// Returns null:
new[] { "ca", "ce" }.LastOrDefault(x => x.Contains("b"));
// Returns null:
new string[0].LastOrDefault();
Pojedynczy()
- Jeśli sekwencja zawiera dokładnie jeden element lub dokładnie jeden element pasujący do podanego
predicate
, ten element jest zwracany. - Jeśli sekwencja nie zawiera elementów lub nie pasuje do podanego
predicate
,InvalidOperationException
jestInvalidOperationException
z komunikatem „Sekwencja nie zawiera elementów”. - Jeśli sekwencja zawiera więcej niż jeden element lub więcej niż jeden element pasujący do podanego
predicate
,InvalidOperationException
jestInvalidOperationException
z komunikatem „Sekwencja zawiera więcej niż jeden element”. - Uwaga: aby ocenić, czy sekwencja zawiera dokładnie jeden element, należy wyliczyć najwyżej dwa elementy.
Przykład
// Returns "a":
new[] { "a" }.Single();
// Throws InvalidOperationException because sequence contains more than one element:
new[] { "a", "b" }.Single();
// Returns "b":
new[] { "a", "b" }.Single(x => x.Equals("b"));
// Throws InvalidOperationException:
new[] { "a", "b" }.Single(x => x.Equals("c"));
// Throws InvalidOperationException:
new string[0].Single();
// Throws InvalidOperationException because sequence contains more than one element:
new[] { "a", "a" }.Single();
SingleOrDefault ()
- Jeśli sekwencja zawiera dokładnie jeden element lub dokładnie jeden element pasujący do podanego
predicate
, ten element jest zwracany. - Jeśli sekwencja nie zawiera elementów lub nie pasuje do podanego
predicate
, zwracana jestdefault(T)
. - Jeśli sekwencja zawiera więcej niż jeden element lub więcej niż jeden element pasujący do podanego
predicate
,InvalidOperationException
jestInvalidOperationException
z komunikatem „Sekwencja zawiera więcej niż jeden element”. - Jeśli sekwencja nie zawiera elementów pasujących do podanego
predicate
, zwraca domyślną wartość typu sekwencji za pomocądefault(T)
. - Uwaga: aby ocenić, czy sekwencja zawiera dokładnie jeden element, należy wyliczyć najwyżej dwa elementy.
Przykład
// Returns "a":
new[] { "a" }.SingleOrDefault();
// returns "a"
new[] { "a", "b" }.SingleOrDefault(x => x == "a");
// Returns null:
new[] { "a", "b" }.SingleOrDefault(x => x == "c");
// Throws InvalidOperationException:
new[] { "a", "a" }.SingleOrDefault(x => x == "a");
// Throws InvalidOperationException:
new[] { "a", "b" }.SingleOrDefault();
// Returns null:
new string[0].SingleOrDefault();
Rekomendacje
Chociaż możesz użyć
FirstOrDefault
,LastOrDefault
lubSingleOrDefault
aby sprawdzić, czy sekwencja zawiera jakieś elementy,Any
lubCount
są bardziej niezawodne. Wynika to z faktu, że zwracana wartośćdefault(T)
z jednej z tych trzech metod nie dowodzi, że sekwencja jest pusta, ponieważ wartość pierwszego / ostatniego / pojedynczego elementu sekwencji może być równiedefault(T)
Zdecyduj, które metody najlepiej pasują do Twojego kodu. Na przykład użyj opcji
Single
tylko wtedy, gdy musisz upewnić się, że w kolekcji znajduje się jeden element pasujący do predykatu - w przeciwnym razie użyj opcjiFirst
; jakoSingle
wyrzuć wyjątek, jeśli sekwencja ma więcej niż jeden pasujący element. Dotyczy to oczywiście także części „* OrDefault”.Odnośnie wydajności: Chociaż często właściwe jest upewnienie się, że jest tylko jeden element (
Single
) lub, albo tylko jeden, albo zero (SingleOrDefault
), zwrócone przez zapytanie, obie te metody wymagają więcej, a często całości, kolekcji do sprawdzenia, aby upewnić się, że nie ma drugiego dopasowania do zapytania. Jest to inaczej niż zachowanie na przykład metodyFirst
, którą można spełnić po znalezieniu pierwszego dopasowania.
Z wyjątkiem
Metoda Except zwraca zestaw elementów, które są zawarte w pierwszej kolekcji, ale nie są zawarte w drugiej. Domyślny IEqualityComparer
służy do porównywania elementów w dwóch zestawach. Istnieje przeciążenie, które przyjmuje argument IEqualityComparer
jako argument.
Przykład:
int[] first = { 1, 2, 3, 4 };
int[] second = { 0, 2, 3, 5 };
IEnumerable<int> inFirstButNotInSecond = first.Except(second);
// inFirstButNotInSecond = { 1, 4 }
Wynik:
1
4
Wersja demonstracyjna na żywo .NET Fiddle
W tym przypadku .Except(second)
wyklucza elementy zawarte w second
tablicy, a mianowicie 2 i 3 (0 i 5 nie są zawarte w first
tablicy i są pomijane).
Zauważ, że „ Except
oznacza Distinct
(tzn. Usuwa powtarzające się elementy). Na przykład:
int[] third = { 1, 1, 1, 2, 3, 4 };
IEnumerable<int> inThirdButNotInSecond = third.Except(second);
// inThirdButNotInSecond = { 1, 4 }
Wynik:
1
4
Wersja demonstracyjna na żywo .NET Fiddle
W takim przypadku elementy 1 i 4 są zwracane tylko raz.
Wdrożenie IEquatable
lub zapewnienie funkcji IEqualityComparer
pozwoli na użycie innej metody porównywania elementów. Należy zauważyć, że metoda GetHashCode
powinna również zostać przesłonięta, aby zwróciła identyczny kod skrótu dla object
które są identyczne zgodnie z implementacją IEquatable
.
Przykład z IEquatable:
class Holiday : IEquatable<Holiday>
{
public string Name { get; set; }
public bool Equals(Holiday other)
{
return Name == other.Name;
}
// GetHashCode must return true whenever Equals returns true.
public override int GetHashCode()
{
//Get hash code for the Name field if it is not null.
return Name?.GetHashCode() ?? 0;
}
}
public class Program
{
public static void Main()
{
List<Holiday> holidayDifference = new List<Holiday>();
List<Holiday> remoteHolidays = new List<Holiday>
{
new Holiday { Name = "Xmas" },
new Holiday { Name = "Hanukkah" },
new Holiday { Name = "Ramadan" }
};
List<Holiday> localHolidays = new List<Holiday>
{
new Holiday { Name = "Xmas" },
new Holiday { Name = "Ramadan" }
};
holidayDifference = remoteHolidays
.Except(localHolidays)
.ToList();
holidayDifference.ForEach(x => Console.WriteLine(x.Name));
}
}
Wynik:
Chanuka
Wersja demonstracyjna na żywo .NET Fiddle
SelectMany: Spłaszczanie sekwencji sekwencji
var sequenceOfSequences = new [] { new [] { 1, 2, 3 }, new [] { 4, 5 }, new [] { 6 } };
var sequence = sequenceOfSequences.SelectMany(x => x);
// returns { 1, 2, 3, 4, 5, 6 }
Użyj SelectMany()
jeśli masz, lub tworzysz sekwencję sekwencji, ale chcesz, aby wynik był jedną długą sekwencją.
W składni zapytania LINQ:
var sequence = from subSequence in sequenceOfSequences
from item in subSequence
select item;
Jeśli masz kolekcję kolekcji i chciałbyś mieć możliwość pracy z danymi z kolekcji rodzica i dziecka w tym samym czasie, jest to również możliwe dzięki SelectMany
.
Zdefiniujmy proste klasy
public class BlogPost
{
public int Id { get; set; }
public string Content { get; set; }
public List<Comment> Comments { get; set; }
}
public class Comment
{
public int Id { get; set; }
public string Content { get; set; }
}
Załóżmy, że mamy następującą kolekcję.
List<BlogPost> posts = new List<BlogPost>()
{
new BlogPost()
{
Id = 1,
Comments = new List<Comment>()
{
new Comment()
{
Id = 1,
Content = "It's really great!",
},
new Comment()
{
Id = 2,
Content = "Cool post!"
}
}
},
new BlogPost()
{
Id = 2,
Comments = new List<Comment>()
{
new Comment()
{
Id = 3,
Content = "I don't think you're right",
},
new Comment()
{
Id = 4,
Content = "This post is a complete nonsense"
}
}
}
};
Teraz chcemy wybrać komentarze Content
wraz z Id
BlogPost
powiązanym z tym komentarzem. W tym celu możemy zastosować odpowiednie przeciążenie SelectMany
.
var commentsWithIds = posts.SelectMany(p => p.Comments, (post, comment) => new { PostId = post.Id, CommentContent = comment.Content });
Nasze commentsWithIds
wygląda
{
PostId = 1,
CommentContent = "It's really great!"
},
{
PostId = 1,
CommentContent = "Cool post!"
},
{
PostId = 2,
CommentContent = "I don't think you're right"
},
{
PostId = 2,
CommentContent = "This post is a complete nonsense"
}
Wybierz wiele
Metoda SelectMany linq „spłaszcza” IEnumerable<IEnumerable<T>>
w IEnumerable<T>
. Wszystkie elementy T w instancjach IEnumerable
zawartych w źródłowym IEnumerable
zostaną połączone w jeden IEnumerable
.
var words = new [] { "a,b,c", "d,e", "f" };
var splitAndCombine = words.SelectMany(x => x.Split(','));
// returns { "a", "b", "c", "d", "e", "f" }
Jeśli użyjesz funkcji selektora, która przekształca elementy wejściowe w sekwencje, wynikiem będą elementy tych sekwencji zwracane jeden po drugim.
Zauważ, że w przeciwieństwie do Select()
liczba elementów na wyjściu nie musi być taka sama jak na wejściu.
Bardziej prawdziwy przykład ze świata
class School
{
public Student[] Students { get; set; }
}
class Student
{
public string Name { get; set; }
}
var schools = new [] {
new School(){ Students = new [] { new Student { Name="Bob"}, new Student { Name="Jack"} }},
new School(){ Students = new [] { new Student { Name="Jim"}, new Student { Name="John"} }}
};
var allStudents = schools.SelectMany(s=> s.Students);
foreach(var student in allStudents)
{
Console.WriteLine(student.Name);
}
Wynik:
Kok
Jacek
Jim
Jan
Wersja demonstracyjna na żywo .NET Fiddle
Wszystko
All
służy do sprawdzania, czy wszystkie elementy kolekcji spełniają warunek, czy nie.
zobacz także: .Any
1. Pusty parametr
Wszystkie : nie można używać z pustym parametrem.
2. Wyrażenie lambda jako parametr
Wszystko : Zwraca true
jeśli wszystkie elementy kolekcji spełniają wyrażenie lambda, a false
przeciwnym razie:
var numbers = new List<int>(){ 1, 2, 3, 4, 5};
bool result = numbers.All(i => i < 10); // true
bool result = numbers.All(i => i >= 3); // false
3. Pusta kolekcja
Wszystko : Zwraca wartość true
jeśli kolekcja jest pusta i podano wyrażenie lambda:
var numbers = new List<int>();
bool result = numbers.All(i => i >= 0); // true
Uwaga: All
zatrzyma iterację kolekcji, gdy tylko znajdzie element niespełniający warunku. Oznacza to, że kolekcja niekoniecznie zostanie w pełni wyliczona; zostanie tylko wyliczony na tyle daleko, aby znaleźć pierwszy element niespełniający warunku.
Kolekcja zapytań według typu / rzutuj elementy na typ
interface IFoo { }
class Foo : IFoo { }
class Bar : IFoo { }
var item0 = new Foo();
var item1 = new Foo();
var item2 = new Bar();
var item3 = new Bar();
var collection = new IFoo[] { item0, item1, item2, item3 };
Korzystanie z OfType
var foos = collection.OfType<Foo>(); // result: IEnumerable<Foo> with item0 and item1
var bars = collection.OfType<Bar>(); // result: IEnumerable<Bar> item item2 and item3
var foosAndBars = collection.OfType<IFoo>(); // result: IEnumerable<IFoo> with all four items
Korzystanie Where
var foos = collection.Where(item => item is Foo); // result: IEnumerable<IFoo> with item0 and item1
var bars = collection.Where(item => item is Bar); // result: IEnumerable<IFoo> with item2 and item3
Korzystanie z Cast
var bars = collection.Cast<Bar>(); // throws InvalidCastException on the 1st item
var foos = collection.Cast<Foo>(); // throws InvalidCastException on the 3rd item
var foosAndBars = collection.Cast<IFoo>(); // OK
Unia
Łączy dwie kolekcje, aby utworzyć odrębną kolekcję przy użyciu domyślnego modułu porównującego równość
int[] numbers1 = { 1, 2, 3 };
int[] numbers2 = { 2, 3, 4, 5 };
var allElement = numbers1.Union(numbers2); // AllElement now contains 1,2,3,4,5
Wersja demonstracyjna na żywo .NET Fiddle
ŁĄCZY
Połączenia służą do łączenia różnych list lub tabel zawierających dane za pomocą wspólnego klucza.
Podobnie jak w SQL, w LINQ obsługiwane są następujące rodzaje złączeń:
Połączenia wewnętrzne, lewe, prawe, krzyżowe i pełne zewnętrzne .
W poniższych przykładach zastosowano dwie listy:
var first = new List<string>(){ "a","b","c"}; // Left data
var second = new List<string>(){ "a", "c", "d"}; // Right data
(Wewnętrzny) Dołącz
var result = from f in first
join s in second on f equals s
select new { f, s };
var result = first.Join(second,
f => f,
s => s,
(f, s) => new { f, s });
// Result: {"a","a"}
// {"c","c"}
Lewe zewnętrzne połączenie
var leftOuterJoin = from f in first
join s in second on f equals s into temp
from t in temp.DefaultIfEmpty()
select new { First = f, Second = t};
// Or can also do:
var leftOuterJoin = from f in first
from s in second.Where(x => x == f).DefaultIfEmpty()
select new { First = f, Second = s};
// Result: {"a","a"}
// {"b", null}
// {"c","c"}
// Left outer join method syntax
var leftOuterJoinFluentSyntax = first.GroupJoin(second,
f => f,
s => s,
(f, s) => new { First = f, Second = s })
.SelectMany(temp => temp.Second.DefaultIfEmpty(),
(f, s) => new { First = f.First, Second = s });
Right Outer Join
var rightOuterJoin = from s in second
join f in first on s equals f into temp
from t in temp.DefaultIfEmpty()
select new {First=t,Second=s};
// Result: {"a","a"}
// {"c","c"}
// {null,"d"}
Cross Join
var CrossJoin = from f in first
from s in second
select new { f, s };
// Result: {"a","a"}
// {"a","c"}
// {"a","d"}
// {"b","a"}
// {"b","c"}
// {"b","d"}
// {"c","a"}
// {"c","c"}
// {"c","d"}
Pełne połączenie zewnętrzne
var fullOuterjoin = leftOuterJoin.Union(rightOuterJoin);
// Result: {"a","a"}
// {"b", null}
// {"c","c"}
// {null,"d"}
Praktyczny przykład
Powyższe przykłady mają prostą strukturę danych, dzięki czemu możesz skupić się na zrozumieniu różnych sprzężeń LINQ pod względem technicznym, ale w prawdziwym świecie masz tabele z kolumnami, które musisz połączyć.
W poniższym przykładzie użyto tylko jednej klasy Region
, w rzeczywistości połączysz dwie lub więcej różnych tabel, które zawierają ten sam klucz (w tym przykładzie first
i second
są połączone za pomocą wspólnego klucza ID
).
Przykład: Rozważ następującą strukturę danych:
public class Region
{
public Int32 ID;
public string RegionDescription;
public Region(Int32 pRegionID, string pRegionDescription=null)
{
ID = pRegionID; RegionDescription = pRegionDescription;
}
}
Teraz przygotuj dane (tj. Zapełnij danymi):
// Left data
var first = new List<Region>()
{ new Region(1), new Region(3), new Region(4) };
// Right data
var second = new List<Region>()
{
new Region(1, "Eastern"), new Region(2, "Western"),
new Region(3, "Northern"), new Region(4, "Southern")
};
Możesz zobaczyć, że w tym przykładzie first
nie zawiera żadnych opisów regionów, więc chcesz dołączyć do nich od second
. Wtedy łączenie wewnętrzne wyglądałoby następująco:
// do the inner join
var result = from f in first
join s in second on f.ID equals s.ID
select new { f.ID, s.RegionDescription };
// Result: {1,"Eastern"}
// {3, Northern}
// {4,"Southern"}
Ten wynik stworzył anonimowe obiekty w locie, co jest w porządku, ale już stworzyliśmy odpowiednią klasę - więc możemy ją określić: Zamiast select new { f.ID, s.RegionDescription };
możemy powiedzieć select new Region(f.ID, s.RegionDescription);
, które zwrócą te same dane, ale utworzą obiekty typu Region
- które utrzymają zgodność z innymi obiektami.
Demo na żywo na skrzypcach .NET
Odrębny
Zwraca unikalne wartości z IEnumerable
. Unikalność jest określana przy użyciu domyślnego modułu porównującego równość.
int[] array = { 1, 2, 3, 4, 2, 5, 3, 1, 2 };
var distinct = array.Distinct();
// distinct = { 1, 2, 3, 4, 5 }
Aby porównać niestandardowy typ danych, musimy zaimplementować interfejs IEquatable<T>
i zapewnić dla tego typu metody GetHashCode
i Equals
. Lub porównywarka równości może zostać nadpisana:
class SSNEqualityComparer : IEqualityComparer<Person> {
public bool Equals(Person a, Person b) => return a.SSN == b.SSN;
public int GetHashCode(Person p) => p.SSN;
}
List<Person> people;
distinct = people.Distinct(SSNEqualityComparer);
Grupa Przez jedno lub wiele pól
Załóżmy, że mamy jakiś model filmowy:
public class Film {
public string Title { get; set; }
public string Category { get; set; }
public int Year { get; set; }
}
Grupuj według właściwości kategorii:
foreach (var grp in films.GroupBy(f => f.Category)) {
var groupCategory = grp.Key;
var numberOfFilmsInCategory = grp.Count();
}
Grupuj według kategorii i roku:
foreach (var grp in films.GroupBy(f => new { Category = f.Category, Year = f.Year })) {
var groupCategory = grp.Key.Category;
var groupYear = grp.Key.Year;
var numberOfFilmsInCategory = grp.Count();
}
Korzystanie z Range z różnymi metodami Linq
Możesz użyć klasy Enumerable obok zapytań Linq, aby przekonwertować pętle na liniowce Linq one.
Wybierz przykład
W przeciwieństwie do robienia tego:
var asciiCharacters = new List<char>();
for (var x = 0; x < 256; x++)
{
asciiCharacters.Add((char)x);
}
Możesz to zrobić:
var asciiCharacters = Enumerable.Range(0, 256).Select(a => (char) a);
Gdzie przykład
W tym przykładzie zostanie wygenerowanych 100 liczb, a nawet zostaną wyodrębnione
var evenNumbers = Enumerable.Range(1, 100).Where(a => a % 2 == 0);
Kolejność zapytań - OrderBy () ThenBy () OrderByDescending () ThenByDescending ()
string[] names= { "mark", "steve", "adam" };
Rosnąco:
Składnia zapytania
var sortedNames =
from name in names
orderby name
select name;
Metoda Składnia
var sortedNames = names.OrderBy(name => name);
sortedNames zawiera nazwy w następującej kolejności: „adam”, „mark”, „steve”
Malejąco:
Składnia zapytania
var sortedNames =
from name in names
orderby name descending
select name;
Metoda Składnia
var sortedNames = names.OrderByDescending(name => name);
sortedNames zawiera nazwy w następującej kolejności: „steve”, „mark”, „adam”
Zamów według kilku pól
Person[] people =
{
new Person { FirstName = "Steve", LastName = "Collins", Age = 30},
new Person { FirstName = "Phil" , LastName = "Collins", Age = 28},
new Person { FirstName = "Adam" , LastName = "Ackerman", Age = 29},
new Person { FirstName = "Adam" , LastName = "Ackerman", Age = 15}
};
Składnia zapytania
var sortedPeople = from person in people
orderby person.LastName, person.FirstName, person.Age descending
select person;
Metoda Składnia
sortedPeople = people.OrderBy(person => person.LastName)
.ThenBy(person => person.FirstName)
.ThenByDescending(person => person.Age);
Wynik
1. Adam Ackerman 29
2. Adam Ackerman 15
3. Phil Collins 28
4. Steve Collins 30
Podstawy
LINQ jest w dużej mierze korzystny przy wyszukiwaniu kolekcji (lub tablic).
Na przykład biorąc pod uwagę następujące przykładowe dane:
var classroom = new Classroom
{
new Student { Name = "Alice", Grade = 97, HasSnack = true },
new Student { Name = "Bob", Grade = 82, HasSnack = false },
new Student { Name = "Jimmy", Grade = 71, HasSnack = true },
new Student { Name = "Greg", Grade = 90, HasSnack = false },
new Student { Name = "Joe", Grade = 59, HasSnack = false }
}
Możemy „zapytać” o te dane przy użyciu składni LINQ. Na przykład, aby odzyskać wszystkich uczniów, którzy mają dziś przekąskę:
var studentsWithSnacks = from s in classroom.Students
where s.HasSnack
select s;
Lub, aby pobrać uczniów z klasą 90 lub wyższą i zwrócić tylko ich nazwiska, a nie pełny obiekt Student
:
var topStudentNames = from s in classroom.Students
where s.Grade >= 90
select s.Name;
Funkcja LINQ składa się z dwóch składni, które wykonują te same funkcje, mają prawie identyczną wydajność, ale są napisane zupełnie inaczej. Składnia w powyższym przykładzie nazywa się składnią zapytania . Poniższy przykład ilustruje jednak składnię metody . Te same dane zostaną zwrócone jak w powyższym przykładzie, ale sposób zapisu zapytania jest inny.
var topStudentNames = classroom.Students
.Where(s => s.Grade >= 90)
.Select(s => s.Name);
Grupuj według
GroupBy to prosty sposób na posortowanie IEnumerable<T>
kolekcji elementów na osobne grupy.
Prosty przykład
W tym pierwszym przykładzie otrzymujemy dwie grupy, nieparzyste i parzyste.
List<int> iList = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var grouped = iList.GroupBy(x => x % 2 == 0);
//Groups iList into odd [13579] and even[2468] items
foreach(var group in grouped)
{
foreach (int item in group)
{
Console.Write(item); // 135792468 (first odd then even)
}
}
Bardziej złożony przykład
Jako przykład weźmy pogrupowanie listy osób według wieku. Najpierw stworzymy obiekt Osoba, który ma dwie właściwości: Nazwę i Wiek.
public class Person
{
public int Age {get; set;}
public string Name {get; set;}
}
Następnie tworzymy naszą przykładową listę osób o różnych nazwiskach i grupach wiekowych.
List<Person> people = new List<Person>();
people.Add(new Person{Age = 20, Name = "Mouse"});
people.Add(new Person{Age = 30, Name = "Neo"});
people.Add(new Person{Age = 40, Name = "Morpheus"});
people.Add(new Person{Age = 30, Name = "Trinity"});
people.Add(new Person{Age = 40, Name = "Dozer"});
people.Add(new Person{Age = 40, Name = "Smith"});
Następnie tworzymy zapytanie LINQ, aby pogrupować naszą listę osób według wieku.
var query = people.GroupBy(x => x.Age);
Robiąc to, możemy zobaczyć Wiek dla każdej grupy i mieć listę każdej osoby w grupie.
foreach(var result in query)
{
Console.WriteLine(result.Key);
foreach(var person in result)
Console.WriteLine(person.Name);
}
Daje to następujące wyniki:
20
Mouse
30
Neo
Trinity
40
Morpheus
Dozer
Smith
Możesz zagrać w demo na żywo w .NET Fiddle
Każdy
Any
służy do sprawdzania, czy jakikolwiek element kolekcji pasuje do warunku, czy nie.
zobacz także: .Wszystkie , Any i FirstOrDefault: najlepsze praktyki
1. Pusty parametr
Any : Zwraca wartość true
jeśli kolekcja ma jakieś elementy, a wartość false
jeśli kolekcja jest pusta:
var numbers = new List<int>();
bool result = numbers.Any(); // false
var numbers = new List<int>(){ 1, 2, 3, 4, 5};
bool result = numbers.Any(); //true
2. Wyrażenie lambda jako parametr
Any : Zwraca wartość true
jeśli kolekcja zawiera jeden lub więcej elementów spełniających warunek wyrażenia lambda:
var arrayOfStrings = new string[] { "a", "b", "c" };
arrayOfStrings.Any(item => item == "a"); // true
arrayOfStrings.Any(item => item == "d"); // false
3. Pusta kolekcja
Any : Zwraca false
jeśli kolekcja jest pusta i podano wyrażenie lambda:
var numbers = new List<int>();
bool result = numbers.Any(i => i >= 0); // false
Uwaga: Any
zatrzyma iterację kolekcji, gdy tylko znajdzie element spełniający warunek. Oznacza to, że kolekcja niekoniecznie zostanie w pełni wyliczona; zostanie tylko wyliczony na tyle daleko, aby znaleźć pierwszy element spełniający warunek.
Wersja demonstracyjna na żywo .NET Fiddle
ToDictionary
Za ToDictionary()
metody LINQ ToDictionary()
można wygenerować kolekcję Dictionary<TKey, TElement>
na podstawie danego źródła IEnumerable<T>
.
IEnumerable<User> users = GetUsers();
Dictionary<int, User> usersById = users.ToDictionary(x => x.Id);
W tym przykładzie pojedynczy argument przekazany do ToDictionary
jest typu Func<TSource, TKey>
, który zwraca klucz dla każdego elementu.
Jest to zwięzły sposób na wykonanie następującej operacji:
Dictionary<int, User> usersById = new Dictionary<int User>();
foreach (User u in users)
{
usersById.Add(u.Id, u);
}
Możesz także przekazać drugi parametr do metody ToDictionary
, która jest typu Func<TSource, TElement>
i zwraca Value
należy dodać dla każdego wpisu.
IEnumerable<User> users = GetUsers();
Dictionary<int, string> userNamesById = users.ToDictionary(x => x.Id, x => x.Name);
Możliwe jest również określenie IComparer
który jest używany do porównywania kluczowych wartości. Może to być przydatne, gdy klucz jest łańcuchem i chcesz, aby pasował on bez rozróżniania wielkości liter.
IEnumerable<User> users = GetUsers();
Dictionary<string, User> usersByCaseInsenstiveName = users.ToDictionary(x => x.Name, StringComparer.InvariantCultureIgnoreCase);
var user1 = usersByCaseInsenstiveName["john"];
var user2 = usersByCaseInsenstiveName["JOHN"];
user1 == user2; // Returns true
Uwaga: metoda ToDictionary
wymaga, aby wszystkie klucze były unikalne, nie może być duplikatów kluczy. Jeśli tak, zgłaszany jest wyjątek: ArgumentException: An item with the same key has already been added.
Jeśli masz scenariusz, w którym wiesz, że będziesz mieć wiele elementów z tym samym kluczem, lepiej skorzystać z ToLookup
.
Agregat
Aggregate
Stosuje funkcję akumulatora w sekwencji.
int[] intList = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int sum = intList.Aggregate((prevSum, current) => prevSum + current);
// sum = 55
- W pierwszym kroku
prevSum = 1
- Przy drugim
prevSum = prevSum(at the first step) + 2
- W i-tym kroku
prevSum = prevSum(at the (i-1) step) + i-th element of the array
krokuprevSum = prevSum(at the (i-1) step) + i-th element of the array
string[] stringList = { "Hello", "World", "!" };
string joinedString = stringList.Aggregate((prev, current) => prev + " " + current);
// joinedString = "Hello World !"
Drugie przeciążenie Aggregate
otrzymuje również parametr seed
, który jest początkową wartością akumulatora. Można tego użyć do obliczenia wielu warunków w kolekcji bez iteracji więcej niż jeden raz.
List<int> items = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
Do kolekcji items
chcemy obliczyć
- Łącznie
.Count
- Liczba liczb parzystych
- Zbierz każdy czwarty przedmiot
Za pomocą Aggregate
można to zrobić w następujący sposób:
var result = items.Aggregate(new { Total = 0, Even = 0, FourthItems = new List<int>() },
(accumelative,item) =>
new {
Total = accumelative.Total + 1,
Even = accumelative.Even + (item % 2 == 0 ? 1 : 0),
FourthItems = (accumelative.Total + 1)%4 == 0 ?
new List<int>(accumelative.FourthItems) { item } :
accumelative.FourthItems
});
// Result:
// Total = 12
// Even = 6
// FourthItems = [4, 8, 12]
Zauważ, że użycie anonimowego typu jako materiału siewnego wymaga utworzenia nowego obiektu dla każdego elementu, ponieważ właściwości są tylko do odczytu. Korzystanie z jednego zwyczaj klasa może po prostu przypisać te informacje i nie new
jest potrzebne (tylko przy podawaniu początkowej seed
parametr
Definiowanie zmiennej w zapytaniu Linq (słowo kluczowe let)
Aby zdefiniować zmienną w wyrażeniu linq, możesz użyć słowa kluczowego let . Zwykle odbywa się to w celu przechowywania wyników pośrednich zapytań cząstkowych, na przykład:
int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var aboveAverages = from number in numbers
let average = numbers.Average()
let nSquared = Math.Pow(number,2)
where nSquared > average
select number;
Console.WriteLine("The average of the numbers is {0}.", numbers.Average());
foreach (int n in aboveAverages)
{
Console.WriteLine("Query result includes number {0} with square of {1}.", n, Math.Pow(n,2));
}
Wynik:
Średnia liczb wynosi 4,5.
Wynik zapytania obejmuje liczbę 3 z kwadratem 9.
Wynik zapytania obejmuje liczbę 4 z kwadratem 16.
Wynik zapytania obejmuje liczbę 5 z kwadratem 25.
Wynik zapytania obejmuje liczbę 6 z kwadratem 36.
Wynik zapytania obejmuje liczbę 7 z kwadratem 49.
Wynik zapytania obejmuje liczbę 8 z kwadratem 64.
Wynik zapytania obejmuje liczbę 9 z kwadratem 81.
SkipWhile
SkipWhile()
służy do wykluczania elementów aż do pierwszego SkipWhile()
może to być sprzeczne z intuicją dla większości)
int[] list = { 42, 42, 6, 6, 6, 42 };
var result = list.SkipWhile(i => i == 42);
// Result: 6, 6, 6, 42
DefaultIfEmpty
DefaultIfEmpty służy do zwracania elementu domyślnego, jeśli sekwencja nie zawiera żadnych elementów. Ten element może być domyślnym typem lub instancją tego typu zdefiniowaną przez użytkownika. Przykład:
var chars = new List<string>() { "a", "b", "c", "d" };
chars.DefaultIfEmpty("N/A").FirstOrDefault(); // returns "a";
chars.Where(str => str.Length > 1)
.DefaultIfEmpty("N/A").FirstOrDefault(); // return "N/A"
chars.Where(str => str.Length > 1)
.DefaultIfEmpty().First(); // returns null;
Wykorzystanie w lewych połączeniach :
Dzięki DefaultIfEmpty
tradycyjne DefaultIfEmpty
Linq może zwrócić domyślny obiekt, jeśli nie znaleziono dopasowania. W ten sposób działa jako lewy łącznik SQL. Przykład:
var leftSequence = new List<int>() { 99, 100, 5, 20, 102, 105 };
var rightSequence = new List<char>() { 'a', 'b', 'c', 'i', 'd' };
var numbersAsChars = from l in leftSequence
join r in rightSequence
on l equals (int)r into leftJoin
from result in leftJoin.DefaultIfEmpty('?')
select new
{
Number = l,
Character = result
};
foreach(var item in numbersAsChars)
{
Console.WriteLine("Num = {0} ** Char = {1}", item.Number, item.Character);
}
ouput:
Num = 99 Char = c
Num = 100 Char = d
Num = 5 Char = ?
Num = 20 Char = ?
Num = 102 Char = ?
Num = 105 Char = i
W przypadku użycia DefaultIfEmpty
(bez określenia wartości domyślnej), co spowoduje brak pasujących elementów we właściwej kolejności, należy upewnić się, że obiekt nie ma null
przed uzyskaniem dostępu do jego właściwości. W przeciwnym razie spowoduje to NullReferenceException
. Przykład:
var leftSequence = new List<int> { 1, 2, 5 };
var rightSequence = new List<dynamic>()
{
new { Value = 1 },
new { Value = 2 },
new { Value = 3 },
new { Value = 4 },
};
var numbersAsChars = (from l in leftSequence
join r in rightSequence
on l equals r.Value into leftJoin
from result in leftJoin.DefaultIfEmpty()
select new
{
Left = l,
// 5 will not have a matching object in the right so result
// will be equal to null.
// To avoid an error use:
// - C# 6.0 or above - ?.
// - Under - result == null ? 0 : result.Value
Right = result?.Value
}).ToList();
SequenceEqual
SequenceEqual
służy do porównywania dwóch IEnumerable<T>
sekwencji IEnumerable<T>
ze sobą.
int[] a = new int[] {1, 2, 3};
int[] b = new int[] {1, 2, 3};
int[] c = new int[] {1, 3, 2};
bool returnsTrue = a.SequenceEqual(b);
bool returnsFalse = a.SequenceEqual(c);
Policz i LongCount
Count
zwraca liczbę elementów w IEnumerable<T>
. Count
udostępnia także opcjonalny parametr predykatu, który pozwala filtrować elementy, które chcesz policzyć.
int[] array = { 1, 2, 3, 4, 2, 5, 3, 1, 2 };
int n = array.Count(); // returns the number of elements in the array
int x = array.Count(i => i > 2); // returns the number of elements in the array greater than 2
LongCount
działa w taki sam sposób jak Count
ale ma typ zwracany long
i jest używany do zliczania IEnumerable<T>
sekwencji IEnumerable<T>
, które są dłuższe niż int.MaxValue
int[] array = GetLargeArray();
long n = array.LongCount(); // returns the number of elements in the array
long x = array.LongCount(i => i > 100); // returns the number of elements in the array greater than 100
Stopniowe budowanie zapytania
Ponieważ LINQ korzysta z odroczonego wykonywania , możemy mieć obiekt zapytania, który tak naprawdę nie zawiera wartości, ale zwróci wartości po ocenie. W ten sposób możemy dynamicznie budować zapytanie w oparciu o nasz przepływ kontroli i oceniać je, gdy skończymy:
IEnumerable<VehicleModel> BuildQuery(int vehicleType, SearchModel search, int start = 1, int count = -1) {
IEnumerable<VehicleModel> query = _entities.Vehicles
.Where(x => x.Active && x.Type == vehicleType)
.Select(x => new VehicleModel {
Id = v.Id,
Year = v.Year,
Class = v.Class,
Make = v.Make,
Model = v.Model,
Cylinders = v.Cylinders ?? 0
});
Możemy warunkowo zastosować filtry:
if (!search.Years.Contains("all", StringComparer.OrdinalIgnoreCase))
query = query.Where(v => search.Years.Contains(v.Year));
if (!search.Makes.Contains("all", StringComparer.OrdinalIgnoreCase)) {
query = query.Where(v => search.Makes.Contains(v.Make));
}
if (!search.Models.Contains("all", StringComparer.OrdinalIgnoreCase)) {
query = query.Where(v => search.Models.Contains(v.Model));
}
if (!search.Cylinders.Equals("all", StringComparer.OrdinalIgnoreCase)) {
decimal minCylinders = 0;
decimal maxCylinders = 0;
switch (search.Cylinders) {
case "2-4":
maxCylinders = 4;
break;
case "5-6":
minCylinders = 5;
maxCylinders = 6;
break;
case "8":
minCylinders = 8;
maxCylinders = 8;
break;
case "10+":
minCylinders = 10;
break;
}
if (minCylinders > 0) {
query = query.Where(v => v.Cylinders >= minCylinders);
}
if (maxCylinders > 0) {
query = query.Where(v => v.Cylinders <= maxCylinders);
}
}
Możemy dodać kolejność sortowania do zapytania na podstawie warunku:
switch (search.SortingColumn.ToLower()) {
case "make_model":
query = query.OrderBy(v => v.Make).ThenBy(v => v.Model);
break;
case "year":
query = query.OrderBy(v => v.Year);
break;
case "engine_size":
query = query.OrderBy(v => v.EngineSize).ThenBy(v => v.Cylinders);
break;
default:
query = query.OrderBy(v => v.Year); //The default sorting.
}
Nasze zapytanie można zdefiniować tak, aby zaczynało się od określonego punktu:
query = query.Skip(start - 1);
i zdefiniowane, aby zwrócić określoną liczbę rekordów:
if (count > -1) {
query = query.Take(count);
}
return query;
}
Gdy mamy już obiekt zapytania, możemy ocenić wyniki za pomocą pętli foreach
lub jednej z metod LINQ, która zwraca zestaw wartości, takich jak ToList
lub ToArray
:
SearchModel sm;
// populate the search model here
// ...
List<VehicleModel> list = BuildQuery(5, sm).ToList();
Zamek błyskawiczny
Metoda rozszerzenia Zip
działa na dwie kolekcje. Łączy każdy element w dwóch seriach razem na podstawie pozycji. W instancji Func
używamy Zip
do obsługi elementów z dwóch kolekcji C # parami. Jeśli seria różni się rozmiarem, dodatkowe elementy większej serii zostaną zignorowane.
Aby wziąć przykład z książki „C # in a Nutshell”,
int[] numbers = { 3, 5, 7 };
string[] words = { "three", "five", "seven", "ignored" };
IEnumerable<string> zip = numbers.Zip(words, (n, w) => n + "=" + w);
Wynik:
3 = trzy
5 = pięć
7 = siedem
GroupJoin ze zmienną zakresu zewnętrznego
Customer[] customers = Customers.ToArray();
Purchase[] purchases = Purchases.ToArray();
var groupJoinQuery =
from c in customers
join p in purchases on c.ID equals p.CustomerID
into custPurchases
select new
{
CustName = c.Name,
custPurchases
};
ElementAt i ElementAtOrDefault
ElementAt
zwróci element o indeksie n
. Jeśli n
nie mieści się w zakresie wyliczalnego, zgłasza ArgumentOutOfRangeException
.
int[] numbers = { 1, 2, 3, 4, 5 };
numbers.ElementAt(2); // 3
numbers.ElementAt(10); // throws ArgumentOutOfRangeException
ElementAtOrDefault
zwróci element o indeksie n
. Jeśli n
nie mieści się w zakresie wyliczalnego, zwraca wartość default(T)
.
int[] numbers = { 1, 2, 3, 4, 5 };
numbers.ElementAtOrDefault(2); // 3
numbers.ElementAtOrDefault(10); // 0 = default(int)
Zarówno ElementAt
jak i ElementAtOrDefault
są zoptymalizowane pod kątem, gdy źródłem jest IList<T>
i w takich przypadkach zostanie zastosowane normalne indeksowanie.
Zauważ, że dla ElementAt
, jeśli podany indeks jest większy niż rozmiar IList<T>
, lista powinna (ale nie jest technicznie gwarantowane) ElementAt
ArgumentOutOfRangeException
.
Kwantyfikatory Linq
Operacje kwantyfikatora zwracają wartość logiczną, jeśli niektóre lub wszystkie elementy w sekwencji spełniają warunek. W tym artykule zobaczymy kilka typowych scenariuszy LINQ to Objects, w których możemy użyć tych operatorów. Istnieją 3 operacje kwantyfikatorów, których można użyć w LINQ:
All
- służy do określenia, czy wszystkie elementy w sekwencji spełniają warunek. Na przykład:
int[] array = { 10, 20, 30 };
// Are all elements >= 10? YES
array.All(element => element >= 10);
// Are all elements >= 20? NO
array.All(element => element >= 20);
// Are all elements < 40? YES
array.All(element => element < 40);
Any
- służy do określenia, czy którykolwiek element w sekwencji spełnia warunek. Na przykład:
int[] query=new int[] { 2, 3, 4 }
query.Any (n => n == 3);
Contains
- służy do ustalenia, czy sekwencja zawiera określony element. Na przykład:
//for int array
int[] query =new int[] { 1,2,3 };
query.Contains(1);
//for string array
string[] query={"Tom","grey"};
query.Contains("Tom");
//for a string
var stringValue="hello";
stringValue.Contains("h");
Łączenie wielu sekwencji
Uwzględnij podmioty Customer
, Purchase
i PurchaseItem
w następujący sposób:
public class Customer
{
public string Id { get; set } // A unique Id that identifies customer
public string Name {get; set; }
}
public class Purchase
{
public string Id { get; set }
public string CustomerId {get; set; }
public string Description { get; set; }
}
public class PurchaseItem
{
public string Id { get; set }
public string PurchaseId {get; set; }
public string Detail { get; set; }
}
Rozważ następujące przykładowe dane dla powyższych podmiotów:
var customers = new List<Customer>()
{
new Customer() {
Id = Guid.NewGuid().ToString(),
Name = "Customer1"
},
new Customer() {
Id = Guid.NewGuid().ToString(),
Name = "Customer2"
}
};
var purchases = new List<Purchase>()
{
new Purchase() {
Id = Guid.NewGuid().ToString(),
CustomerId = customers[0].Id,
Description = "Customer1-Purchase1"
},
new Purchase() {
Id = Guid.NewGuid().ToString(),
CustomerId = customers[0].Id,
Description = "Customer1-Purchase2"
},
new Purchase() {
Id = Guid.NewGuid().ToString(),
CustomerId = customers[1].Id,
Description = "Customer2-Purchase1"
},
new Purchase() {
Id = Guid.NewGuid().ToString(),
CustomerId = customers[1].Id,
Description = "Customer2-Purchase2"
}
};
var purchaseItems = new List<PurchaseItem>()
{
new PurchaseItem() {
Id = Guid.NewGuid().ToString(),
PurchaseId= purchases[0].Id,
Detail = "Purchase1-PurchaseItem1"
},
new PurchaseItem() {
Id = Guid.NewGuid().ToString(),
PurchaseId= purchases[1].Id,
Detail = "Purchase2-PurchaseItem1"
},
new PurchaseItem() {
Id = Guid.NewGuid().ToString(),
PurchaseId= purchases[1].Id,
Detail = "Purchase2-PurchaseItem2"
},
new PurchaseItem() {
Id = Guid.NewGuid().ToString(),
PurchaseId= purchases[3].Id,
Detail = "Purchase3-PurchaseItem1"
}
};
Teraz rozważ poniższe zapytanie linq:
var result = from c in customers
join p in purchases on c.Id equals p.CustomerId // first join
join pi in purchaseItems on p.Id equals pi.PurchaseId // second join
select new
{
c.Name, p.Description, pi.Detail
};
Aby wyświetlić wynik powyższego zapytania:
foreach(var resultItem in result)
{
Console.WriteLine($"{resultItem.Name}, {resultItem.Description}, {resultItem.Detail}");
}
Dane wyjściowe zapytania będą następujące:
Klient1, Klient1-Zakup1, Zakup1-ZakupItem1
Klient1, Klient1-Zakup2, Zakup2-ZakupItem1
Klient1, Klient1-Zakup2, Zakup2-ZakupItem2
Klient2, Klient2-Zakup2, Zakup3-ZakupItem1
Wersja demonstracyjna na żywo .NET Fiddle
Łączenie wielu kluczy
PropertyInfo[] stringProps = typeof (string).GetProperties();//string properties
PropertyInfo[] builderProps = typeof(StringBuilder).GetProperties();//stringbuilder properties
var query =
from s in stringProps
join b in builderProps
on new { s.Name, s.PropertyType } equals new { b.Name, b.PropertyType }
select new
{
s.Name,
s.PropertyType,
StringToken = s.MetadataToken,
StringBuilderToken = b.MetadataToken
};
Zauważ, że anonimowe typy w powyższym join
muszą zawierać te same właściwości, ponieważ obiekty są uważane za równe tylko wtedy, gdy wszystkie ich właściwości są równe. W przeciwnym razie zapytanie nie zostanie skompilowane.
Wybierz za pomocą Func selektor - służy do uzyskiwania rankingu elementów
Z przeciążeń metod rozszerzenia Select
przechodzi również index
bieżącego elementu w kolekcji, która jest select
ed. Oto kilka jego zastosowań.
Uzyskaj „numer wiersza” elementów
var rowNumbers = collection.OrderBy(item => item.Property1)
.ThenBy(item => item.Property2)
.ThenByDescending(item => item.Property3)
.Select((item, index) => new { Item = item, RowNumber = index })
.ToList();
Uzyskaj rangę przedmiotu w grupie
var rankInGroup = collection.GroupBy(item => item.Property1)
.OrderBy(group => group.Key)
.SelectMany(group => group.OrderBy(item => item.Property2)
.ThenByDescending(item => item.Property3)
.Select((item, index) => new
{
Item = item,
RankInGroup = index
})).ToList();
Uzyskaj ranking grup (znany również w Oracle jako dense_rank)
var rankOfBelongingGroup = collection.GroupBy(item => item.Property1)
.OrderBy(group => group.Key)
.Select((group, index) => new
{
Items = group,
Rank = index
})
.SelectMany(v => v.Items, (s, i) => new
{
Item = i,
DenseRank = s.Rank
}).ToList();
Do przetestowania tego możesz użyć:
public class SomeObject
{
public int Property1 { get; set; }
public int Property2 { get; set; }
public int Property3 { get; set; }
public override string ToString()
{
return string.Join(", ", Property1, Property2, Property3);
}
}
I dane:
List<SomeObject> collection = new List<SomeObject>
{
new SomeObject { Property1 = 1, Property2 = 1, Property3 = 1},
new SomeObject { Property1 = 1, Property2 = 2, Property3 = 1},
new SomeObject { Property1 = 1, Property2 = 2, Property3 = 2},
new SomeObject { Property1 = 2, Property2 = 1, Property3 = 1},
new SomeObject { Property1 = 2, Property2 = 2, Property3 = 1},
new SomeObject { Property1 = 2, Property2 = 2, Property3 = 1},
new SomeObject { Property1 = 2, Property2 = 3, Property3 = 1}
};
TakeWhile
TakeWhile
zwraca elementy z sekwencji, o ile warunek jest spełniony
int[] list = { 1, 10, 40, 50, 44, 70, 4 };
var result = list.TakeWhile(item => item < 50).ToList();
// result = { 1, 10, 40 }
Suma
Enumerable.Sum
rozszerzenia Enumerable.Sum
oblicza sumę wartości liczbowych.
W przypadku, gdy same elementy kolekcji są liczbami, można bezpośrednio obliczyć sumę.
int[] numbers = new int[] { 1, 4, 6 };
Console.WriteLine( numbers.Sum() ); //outputs 11
W przypadku, gdy typ elementów jest typem złożonym, możesz użyć wyrażenia lambda, aby określić wartość, którą należy obliczyć:
var totalMonthlySalary = employees.Sum( employee => employee.MonthlySalary );
Metodę rozszerzenia sumy można obliczyć za pomocą następujących typów:
- Int32
- Int64
- Pojedynczy
- Podwójnie
- Dziesiętny
Jeśli twoja kolekcja zawiera typy zerowalne, możesz użyć operatora zerowania koalescencji, aby ustawić wartość domyślną dla elementów zerowych:
int?[] numbers = new int?[] { 1, null, 6 };
Console.WriteLine( numbers.Sum( number => number ?? 0 ) ); //outputs 7
ToLookup
ToLookup zwraca strukturę danych, która umożliwia indeksowanie. Jest to metoda rozszerzenia. Tworzy instancję ILookup, którą można indeksować lub wyliczać za pomocą pętli foreach. Wpisy są łączone w grupy przy każdym kluczu. - dotnetperls
string[] array = { "one", "two", "three" };
//create lookup using string length as key
var lookup = array.ToLookup(item => item.Length);
//join the values whose lengths are 3
Console.WriteLine(string.Join(",",lookup[3]));
//output: one,two
Inny przykład:
int[] array = { 1,2,3,4,5,6,7,8 };
//generate lookup for odd even numbers (keys will be 0 and 1)
var lookup = array.ToLookup(item => item % 2);
//print even numbers after joining
Console.WriteLine(string.Join(",",lookup[0]));
//output: 2,4,6,8
//print odd numbers after joining
Console.WriteLine(string.Join(",",lookup[1]));
//output: 1,3,5,7
Zbuduj własne operatory Linq dla IEnumerable
Jedną z wielkich zalet Linq jest to, że można go łatwo rozbudować. Musisz tylko utworzyć metodę rozszerzenia, której argument to IEnumerable<T>
.
public namespace MyNamespace
{
public static class LinqExtensions
{
public static IEnumerable<List<T>> Batch<T>(this IEnumerable<T> source, int batchSize)
{
var batch = new List<T>();
foreach (T item in source)
{
batch.Add(item);
if (batch.Count == batchSize)
{
yield return batch;
batch = new List<T>();
}
}
if (batch.Count > 0)
yield return batch;
}
}
}
Ten przykład dzieli elementy w IEnumerable<T>
na listy o ustalonym rozmiarze, przy czym ostatnia lista zawiera resztę elementów. Zwróć uwagę, w jaki sposób obiekt, do którego zastosowano metodę rozszerzenia, jest przekazywany ( source
argumentu) jako argument początkowy przy użyciu this
słowa kluczowego. Następnie słowo kluczowe yield
służy do wyprowadzenia następnego elementu na wyjściu IEnumerable<T>
przed kontynuowaniem wykonywania od tego momentu (patrz słowo kluczowe fed ).
Ten przykład zostałby użyty w twoim kodzie w następujący sposób:
//using MyNamespace;
var items = new List<int> { 2, 3, 4, 5, 6 };
foreach (List<int> sublist in items.Batch(3))
{
// do something
}
W pierwszej pętli podlistą będzie {2, 3, 4}
a w drugiej {5, 6}
.
Niestandardowe metody LinQ można również łączyć ze standardowymi metodami LinQ. na przykład:
//using MyNamespace;
var result = Enumerable.Range(0, 13) // generate a list
.Where(x => x%2 == 0) // filter the list or do something other
.Batch(3) // call our extension method
.ToList() // call other standard methods
To zapytanie zwróci liczby parzyste zgrupowane w partiach o rozmiarze 3: {0, 2, 4}, {6, 8, 10}, {12}
Pamiętaj, że potrzebujesz using MyNamespace;
wiersz, aby mieć dostęp do metody rozszerzenia.
Używanie SelectMany zamiast zagnieżdżonych pętli
Biorąc pod uwagę 2 listy
var list1 = new List<string> { "a", "b", "c" };
var list2 = new List<string> { "1", "2", "3", "4" };
jeśli chcesz wyprowadzić wszystkie permutacje, możesz użyć zagnieżdżonych pętli, takich jak
var result = new List<string>();
foreach (var s1 in list1)
foreach (var s2 in list2)
result.Add($"{s1}{s2}");
Za pomocą SelectMany możesz wykonać tę samą operację, co
var result = list1.SelectMany(x => list2.Select(y => $"{x}{y}", x, y)).ToList();
Any and First (OrDefault) - najlepsza praktyka
Nie wyjaśnię, co robią Any
i FirstOrDefault
, ponieważ istnieją już dwa dobre przykłady na ich temat. Aby uzyskać więcej informacji, zobacz Dowolny i pierwszy, FirstOrDefault, Last, LastOrDefault, Single i SingleOrDefault .
Często widuję w kodzie wzór, którego należy unikać
if (myEnumerable.Any(t=>t.Foo == "Bob"))
{
var myFoo = myEnumerable.First(t=>t.Foo == "Bob");
//Do stuff
}
Można to napisać bardziej efektywnie w ten sposób
var myFoo = myEnumerable.FirstOrDefault(t=>t.Foo == "Bob");
if (myFoo != null)
{
//Do stuff
}
Korzystając z drugiego przykładu, kolekcja jest przeszukiwana tylko raz i daje taki sam wynik jak pierwszy. Ten sam pomysł można zastosować do Single
.
GroupBy Sum and Count
Weźmy przykładową klasę:
public class Transaction
{
public string Category { get; set; }
public DateTime Date { get; set; }
public decimal Amount { get; set; }
}
Rozważmy teraz listę transakcji:
var transactions = new List<Transaction>
{
new Transaction { Category = "Saving Account", Amount = 56, Date = DateTime.Today.AddDays(1) },
new Transaction { Category = "Saving Account", Amount = 10, Date = DateTime.Today.AddDays(-10) },
new Transaction { Category = "Credit Card", Amount = 15, Date = DateTime.Today.AddDays(1) },
new Transaction { Category = "Credit Card", Amount = 56, Date = DateTime.Today },
new Transaction { Category = "Current Account", Amount = 100, Date = DateTime.Today.AddDays(5) },
};
Jeśli chcesz obliczyć kategorię pod względem sumy i liczby, możesz użyć GroupBy w następujący sposób:
var summaryApproach1 = transactions.GroupBy(t => t.Category)
.Select(t => new
{
Category = t.Key,
Count = t.Count(),
Amount = t.Sum(ta => ta.Amount),
}).ToList();
Console.WriteLine("-- Summary: Approach 1 --");
summaryApproach1.ForEach(
row => Console.WriteLine($"Category: {row.Category}, Amount: {row.Amount}, Count: {row.Count}"));
Alternatywnie możesz to zrobić w jednym kroku:
var summaryApproach2 = transactions.GroupBy(t => t.Category, (key, t) =>
{
var transactionArray = t as Transaction[] ?? t.ToArray();
return new
{
Category = key,
Count = transactionArray.Length,
Amount = transactionArray.Sum(ta => ta.Amount),
};
}).ToList();
Console.WriteLine("-- Summary: Approach 2 --");
summaryApproach2.ForEach(
row => Console.WriteLine($"Category: {row.Category}, Amount: {row.Amount}, Count: {row.Count}"));
Dane wyjściowe dla obu powyższych zapytań byłyby takie same:
Kategoria: Konto oszczędnościowe, Kwota: 66, Liczba: 2
Kategoria: karta kredytowa, kwota: 71, liczba: 2
Kategoria: rachunek bieżący, kwota: 100, liczba: 1
Rewers
- Odwraca kolejność elementów w sekwencji.
- Jeśli nie ma żadnych elementów, zgłasza
ArgumentNullException: source is null.
Przykład:
// Create an array.
int[] array = { 1, 2, 3, 4 }; //Output:
// Call reverse extension method on the array. //4
var reverse = array.Reverse(); //3
// Write contents of array to screen. //2
foreach (int value in reverse) //1
Console.WriteLine(value);
Pamiętaj, że funkcja Reverse()
może działać inaczej w zależności od kolejności łańcucha instrukcji LINQ.
//Create List of chars
List<int> integerlist = new List<int>() { 1, 2, 3, 4, 5, 6 };
//Reversing the list then taking the two first elements
IEnumerable<int> reverseFirst = integerlist.Reverse<int>().Take(2);
//Taking 2 elements and then reversing only thos two
IEnumerable<int> reverseLast = integerlist.Take(2).Reverse();
//reverseFirst output: 6, 5
//reverseLast output: 2, 1
Funkcja Reverse () działa na buforowanie wszystkiego, a następnie przechodzenie przez nie do tyłu, co nie jest zbyt wydajne, ale z tego punktu widzenia OrderBy również nie jest.
W LINQ-to-Objects są operacje buforowania (Reverse, OrderBy, GroupBy itp.) Oraz operacje niebuforowane (Where, Take, Skip, itp.).
Przykład: nie buforujące rozszerzenie odwrotne
public static IEnumerable<T> Reverse<T>(this IList<T> list) {
for (int i = list.Count - 1; i >= 0; i--)
yield return list[i];
}
Ta metoda może napotkać problemy, jeśli mutujesz listę podczas iteracji.
Wyliczanie wyliczalnego
Interfejs IEnumerable <T> jest interfejsem podstawowym wszystkich ogólnych modułów wyliczających i jest kwintesencją zrozumienia LINQ. W swoim rdzeniu reprezentuje sekwencję.
Ten podstawowy interfejs jest dziedziczony przez wszystkie ogólne kolekcje, takie jak Collection <T> , Array , List <T> , Dictionary <TKey, TValue> Class i HashSet <T> .
Oprócz reprezentowania sekwencji każda klasa dziedzicząca z IEnumerable <T> musi udostępnić IEnumerator <T>. Moduł wyliczający ujawnia iterator dla elementu wyliczeniowego, a te dwa wzajemnie powiązane interfejsy i idee są źródłem powiedzenia „wyliczenie elementu wyliczeniowego”.
„Wyliczanie wyliczalnego” jest ważnym zwrotem. Wyliczalna jest po prostu strukturą do iteracji, nie zawiera żadnych zmaterializowanych obiektów. Na przykład, podczas sortowania, wyliczanie może zawierać kryteria pola do sortowania, ale .OrderBy()
użycie .OrderBy()
zwróci IEnumerable <T>, który wie tylko jak sortować. Korzystanie z wywołania, które zmaterializuje obiekty, podobnie jak w iteracji zestawu, jest znane jako wyliczanie (na przykład .ToList()
). Proces wyliczania będzie wykorzystywał wymienną definicję tego, jak poruszać się po serii i zwracać odpowiednie obiekty (w kolejności, filtrowane, rzutowane itp.).
Dopiero po wyliczeniu wyliczenia powoduje materializację obiektów, to jest wtedy, gdy mierniki takie jak złożoność czasu (jak długo powinno to zająć w związku z rozmiarem szeregu) i złożoność przestrzenna (ile miejsca powinien użyć w związku z rozmiarem szeregu) być mierzonym.
Utworzenie własnej klasy, która dziedziczy po IEnumerable <T>, może być nieco skomplikowane w zależności od podstawowej serii, która musi być policzalna. Ogólnie najlepiej jest użyć jednej z istniejących kolekcji ogólnych. To powiedziawszy, możliwe jest również dziedziczenie z IEnumerable interfejsu <T> bez posiadania zdefiniowanej tablicy jako podstawowej struktury.
Na przykład, używając serii Fibonacciego jako podstawowej sekwencji. Zauważ, że wywołanie Where
po prostu buduje IEnumerable
, i dopiero po wywołaniu wyliczenia tego enumerable zostanie wykonana dowolna z tych wartości.
void Main()
{
Fibonacci Fibo = new Fibonacci();
IEnumerable<long> quadrillionplus = Fibo.Where(i => i > 1000000000000);
Console.WriteLine("Enumerable built");
Console.WriteLine(quadrillionplus.Take(2).Sum());
Console.WriteLine(quadrillionplus.Skip(2).First());
IEnumerable<long> fibMod612 = Fibo.OrderBy(i => i % 612);
Console.WriteLine("Enumerable built");
Console.WriteLine(fibMod612.First());//smallest divisible by 612
}
public class Fibonacci : IEnumerable<long>
{
private int max = 90;
//Enumerator called typically from foreach
public IEnumerator GetEnumerator() {
long n0 = 1;
long n1 = 1;
Console.WriteLine("Enumerating the Enumerable");
for(int i = 0; i < max; i++){
yield return n0+n1;
n1 += n0;
n0 = n1-n0;
}
}
//Enumerable called typically from linq
IEnumerator<long> IEnumerable<long>.GetEnumerator() {
long n0 = 1;
long n1 = 1;
Console.WriteLine("Enumerating the Enumerable");
for(int i = 0; i < max; i++){
yield return n0+n1;
n1 += n0;
n0 = n1-n0;
}
}
}
Wynik
Enumerable built
Enumerating the Enumerable
4052739537881
Enumerating the Enumerable
4052739537881
Enumerable built
Enumerating the Enumerable
14930352
Siła w drugim zbiorze (fibMod612) polega na tym, że chociaż wykonaliśmy wywołanie, aby zamówić cały nasz zestaw liczb Fibonacciego, ponieważ za pomocą .First()
tylko jedną wartość, złożoność czasowa wynosiła O (n) jako tylko 1 wartość należy porównać podczas wykonywania algorytmu zamawiania. Wynika to z faktu, że nasz moduł wyliczający poprosił tylko o 1 wartość, a zatem nie trzeba było zmaterializować całego wyliczenia. Gdybyśmy użyli .Take(5)
zamiast .First()
wyliczający poprosiłby o 5 wartości, a najwyżej 5 wartości musiałoby się zmaterializować. W porównaniu z koniecznością zamówienia całego zestawu, a następnie wzięcia pierwszych 5 wartości, zasada oszczędza dużo czasu i miejsca wykonania.
Zamów przez
Zamawia kolekcję według określonej wartości.
Gdy wartość jest liczbą całkowitą , podwójną lub zmiennoprzecinkową , zaczyna się od wartości minimalnej , co oznacza, że najpierw otrzymujesz wartości ujemne, niż zero, a następnie wartości dodatnie (patrz Przykład 1).
Gdy zamawiasz za pomocą znaku, metoda porównuje wartości ascii znaków w celu posortowania kolekcji (patrz Przykład 2).
Podczas sortowania ciągów metoda OrderBy porównuje je, patrząc na ich CultureInfo, ale zwykle zaczyna się od pierwszej litery w alfabecie (a, b, c ...).
Tego rodzaju porządek nazywa się rosnąco, jeśli chcesz go odwrotnie, musisz zejść (patrz OrderByDescending).
Przykład 1:
int[] numbers = {2, 1, 0, -1, -2};
IEnumerable<int> ascending = numbers.OrderBy(x => x);
// returns {-2, -1, 0, 1, 2}
Przykład 2:
char[] letters = {' ', '!', '?', '[', '{', '+', '1', '9', 'a', 'A', 'b', 'B', 'y', 'Y', 'z', 'Z'};
IEnumerable<char> ascending = letters.OrderBy(x => x);
// returns { ' ', '!', '+', '1', '9', '?', 'A', 'B', 'Y', 'Z', '[', 'a', 'b', 'y', 'z', '{' }
Przykład:
class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
var people = new[]
{
new Person {Name = "Alice", Age = 25},
new Person {Name = "Bob", Age = 21},
new Person {Name = "Carol", Age = 43}
};
var youngestPerson = people.OrderBy(x => x.Age).First();
var name = youngestPerson.Name; // Bob
OrderByDescending
Zamawia kolekcję według określonej wartości.
Kiedy wartość jest liczbą całkowitą , podwójną lub zmiennoprzecinkową , zaczyna się od wartości maksymalnej , co oznacza, że najpierw otrzymujesz wartości dodatnie, niż zero, a następnie wartości ujemne (patrz Przykład 1).
Gdy zamawiasz za pomocą znaku, metoda porównuje wartości ascii znaków w celu posortowania kolekcji (patrz Przykład 2).
Podczas sortowania ciągów metoda OrderBy porównuje je, patrząc na ich CultureInfo, ale zwykle zaczyna się od ostatniej litery w alfabecie (z, y, x, ...).
Ten rodzaj porządku nazywa się malejącym, jeśli chcesz go na odwrót, musisz rosnąć (patrz OrderBy).
Przykład 1:
int[] numbers = {-2, -1, 0, 1, 2};
IEnumerable<int> descending = numbers.OrderByDescending(x => x);
// returns {2, 1, 0, -1, -2}
Przykład 2:
char[] letters = {' ', '!', '?', '[', '{', '+', '1', '9', 'a', 'A', 'b', 'B', 'y', 'Y', 'z', 'Z'};
IEnumerable<char> descending = letters.OrderByDescending(x => x);
// returns { '{', 'z', 'y', 'b', 'a', '[', 'Z', 'Y', 'B', 'A', '?', '9', '1', '+', '!', ' ' }
Przykład 3:
class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
var people = new[]
{
new Person {Name = "Alice", Age = 25},
new Person {Name = "Bob", Age = 21},
new Person {Name = "Carol", Age = 43}
};
var oldestPerson = people.OrderByDescending(x => x.Age).First();
var name = oldestPerson.Name; // Carol
Concat
Scala dwie kolekcje (bez usuwania duplikatów)
List<int> foo = new List<int> { 1, 2, 3 };
List<int> bar = new List<int> { 3, 4, 5 };
// Through Enumerable static class
var result = Enumerable.Concat(foo, bar).ToList(); // 1,2,3,3,4,5
// Through extension method
var result = foo.Concat(bar).ToList(); // 1,2,3,3,4,5
Zawiera
MSDN:
Określa, czy sekwencja zawiera określony element, używając określonego
IEqualityComparer<T>
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
var result1 = numbers.Contains(4); // true
var result2 = numbers.Contains(8); // false
List<int> secondNumberCollection = new List<int> { 4, 5, 6, 7 };
// Note that can use the Intersect method in this case
var result3 = secondNumberCollection.Where(item => numbers.Contains(item)); // will be true only for 4,5
Korzystanie z obiektu zdefiniowanego przez użytkownika:
public class Person
{
public string Name { get; set; }
}
List<Person> objects = new List<Person>
{
new Person { Name = "Nikki"},
new Person { Name = "Gilad"},
new Person { Name = "Phil"},
new Person { Name = "John"}
};
//Using the Person's Equals method - override Equals() and GetHashCode() - otherwise it
//will compare by reference and result will be false
var result4 = objects.Contains(new Person { Name = "Phil" }); // true
Korzystanie z Enumerable.Contains(value, comparer)
:
public class Compare : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
return x.Name == y.Name;
}
public int GetHashCode(Person codeh)
{
return codeh.Name.GetHashCode();
}
}
var result5 = objects.Contains(new Person { Name = "Phil" }, new Compare()); // true
Inteligentnym zastosowaniem Contains
byłoby zastąpienie wielu klauzul if
w wywołaniu Contains
.
Zamiast tego:
if(status == 1 || status == 3 || status == 4)
{
//Do some business operation
}
else
{
//Do something else
}
Zrób to:
if(new int[] {1, 3, 4 }.Contains(status)
{
//Do some business operaion
}
else
{
//Do something else
}