C# Language
LINQ-vragen
Zoeken…
Invoering
LINQ is een acroniem dat staat voor L anguage IN tegrated Q uery. Het is een concept dat een zoektaal integreert door een consistent model te bieden voor het werken met gegevens in verschillende soorten gegevensbronnen en -indelingen; u gebruikt dezelfde basiscoderingspatronen om gegevens in XML-documenten, SQL-databases, ADO.NET-gegevenssets, .NET-verzamelingen en elk ander formaat waarvoor een LINQ-provider beschikbaar is, op te vragen en te transformeren.
Syntaxis
Syntaxis van zoekopdracht:
- van <bereik variabele> in <collectie>
- [uit <bereikvariabele> in <collectie>, ...]
- <filteren, samenvoegen, groeperen, aggregaten, ...> <lambda-expressie>
- <selecteer of groep Door operator> <formuleer het resultaat>
Methode syntaxis:
- Enumerable.Aggregate (func)
- Opsombaar. Samenvoegen (zaad, func)
- Enumerable.Aggregate (seed, func, resultSelector)
- Enumerable.All (predikaat)
- Enumerable.Any ()
- Enumerable.Any (predikaat)
- Enumerable.AsEnumerable ()
- Enumerable.Average ()
- Enumerable.Average (selector)
- Enumerable.Cast <Resultaten> ()
- Enumerable.Concat (tweede)
- Enumerable.Contains (value)
- Enumerable.Contains (value, comparer)
- Enumerable.Count ()
- Enumerable.Count (predikaat)
- Enumerable.DefaultIfEmpty ()
- Enumerable.DefaultIfEmpty (defaultValue)
- Enumerable.Distinct ()
- Enumerable.Distinct (comparator)
- Enumerable.ElementAt (index)
- Enumerable.ElementAtOrDefault (index)
- Enumerable.Empty ()
- Enumerable.Except (tweede)
- Enumerable.Except (tweede, vergelijker)
- Enumerable.First ()
- Enumerable.First (predikaat)
- Enumerable.FirstOrDefault ()
- Enumerable.FirstOrDefault (predikaat)
- Enumerable.GroupBy (keySelector)
- Enumerable.GroupBy (keySelector, resultSelector)
- Enumerable.GroupBy (keySelector, elementSelector)
- Enumerable.GroupBy (keySelector, comparer)
- Enumerable.GroupBy (keySelector, resultSelector, comparer)
- Enumerable.GroupBy (keySelector, elementSelector, resultSelector)
- Enumerable.GroupBy (keySelector, elementSelector, comparer)
- Enumerable.GroupBy (keySelector, elementSelector, resultSelector, comparer)
- Enumerable.Intersect (tweede)
- Enumerable.Intersect (tweede, vergelijker)
- Enumerable.Join (inner, outerKeySelector, innerKeySelector, resultSelector)
- Enumerable.Join (inner, outerKeySelector, innerKeySelector, resultSelector, comparer)
- Enumerable.Last ()
- Enumerable.Last (predikaat)
- Enumerable.LastOrDefault ()
- Enumerable.LastOrDefault (predikaat)
- Enumerable.LongCount ()
- Enumerable.LongCount (predikaat)
- Enumerable.Max ()
- Enumerable.Max (selector)
- Enumerable.Min ()
- Enumerable.Min (selector)
- Enumerable.OfType <TResult> ()
- Enumerable.OrderBy (keySelector)
- Enumerable.OrderBy (keySelector, comparer)
- Enumerable.OrderByDescending (keySelector)
- Enumerable.OrderByDescending (keySelector, comparer)
- Enumerable.Range (start, count)
- Enumerable.Repeat (element, count)
- Enumerable.Reverse ()
- Enumerable.Select (selector)
- Enumerable.SelectMany (selector)
- Enumerable.SelectMany (collectionSelector, resultSelector)
- Enumerable.SequenceEqual (tweede)
- Enumerable.SequenceEqual (tweede, vergelijker)
- Enumerable.Single ()
- Enumerable.Single (predikaat)
- Enumerable.SingleOrDefault ()
- Enumerable.SingleOrDefault (predikaat)
- Enumerable.Skip (count)
- Enumerable.SkipWhile (predikaat)
- Enumerable.Sum ()
- Enumerable.Sum (selector)
- Enumerable.Take (count)
- Enumerable.TakeWhile (predikaat)
- orderedEnumerable.ThenBy (keySelector)
- besteldEnumerable.ThenBy (keySelector, comparer)
- orderedEnumerable.ThenByDescending (keySelector)
- orderEnumerable.ThenByDescending (keySelector, comparer)
- Enumerable.ToArray ()
- Enumerable.ToDictionary (keySelector)
- Enumerable.ToDictionary (keySelector, elementSelector)
- Enumerable.ToDictionary (keySelector, comparer)
- Enumerable.ToDictionary (keySelector, elementSelector, comparer)
- Enumerable.ToList ()
- Enumerable.ToLookup (keySelector)
- Enumerable.ToLookup (keySelector, elementSelector)
- Enumerable.ToLookup (keySelector, comparer)
- Enumerable.ToLookup (keySelector, elementSelector, comparer)
- Enumerable.Union (tweede)
- Enumerable.Union (tweede, vergelijker)
- Enumerable.Where (predikaat)
- Enumerable.Zip (tweede, resultSelector)
Opmerkingen
Als u LINQ-query's wilt gebruiken, moet u System.Linq
importeren.
De methode-syntaxis is krachtiger en flexibeler, maar de query-syntaxis is misschien eenvoudiger en meer vertrouwd. Alle query's die in de syntaxis van de query zijn geschreven, worden door de compiler in de functionele syntaxis vertaald, dus de prestaties zijn hetzelfde.
Query-objecten worden niet geëvalueerd totdat ze worden gebruikt, dus ze kunnen zonder prestatieverlies worden gewijzigd of aangevuld.
Waar
Retourneert een subset van items waarvoor het opgegeven predicaat waar is.
List<string> trees = new List<string>{ "Oak", "Birch", "Beech", "Elm", "Hazel", "Maple" };
Methode syntaxis
// Select all trees with name of length 3
var shortTrees = trees.Where(tree => tree.Length == 3); // Oak, Elm
Syntaxis van zoekopdracht
var shortTrees = from tree in trees
where tree.Length == 3
select tree; // Oak, Elm
Selecteer - Elementen transformeren
Met Select kunt u een transformatie toepassen op elk element in elke gegevensstructuur die IEnumerable implementeert.
Het eerste karakter van elke string in de volgende lijst krijgen:
List<String> trees = new List<String>{ "Oak", "Birch", "Beech", "Elm", "Hazel", "Maple" };
Gebruik van reguliere (lambda) syntaxis
//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);
}
Output:
O
B
B
E
H
M
LINQ Query Syntax gebruiken
initials = from tree in trees
select tree.Substring(0, 1);
Chaining methoden
Veel LINQ-functies werken beide op een IEnumerable<TSource>
en retourneren ook een IEnumerable<TResult>
. De TSource
en TResult
kunnen al dan niet naar hetzelfde type verwijzen, afhankelijk van de methode in kwestie en eventuele functies die eraan worden doorgegeven.
Een paar voorbeelden hiervan zijn
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
)
Hoewel voor sommige methodeketting een volledige set moet worden bewerkt voordat wordt doorgegaan, profiteert LINQ van uitgestelde uitvoering door MSDN met opbrengstteruggave te gebruiken die achter de schermen een Enumerable en een Enumerator creëert. Het proces van ketenvorming in LINQ is in wezen het bouwen van een opsomming (iterator) voor de originele set - die wordt uitgesteld - totdat deze wordt gematerialiseerd door de opsomming op te sommen .
Hierdoor kunnen deze functies vloeiend worden gekoppeld wiki , waarbij de ene functie direct kan werken op het resultaat van de andere. Deze codestijl kan worden gebruikt om veel op een reeks gebaseerde bewerkingen in één instructie uit te voeren.
Het is bijvoorbeeld mogelijk om Select
, Where
en OrderBy
te combineren om een reeks in een enkele instructie te transformeren, filteren en sorteren.
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
Output:
2
4
8
Alle functies die het generieke type IEnumerable<T>
zowel uitbreiden als retourneren, kunnen als kettingclausules in een enkele instructie worden gebruikt. Deze stijl van vloeiend programmeren is krachtig en moet worden overwogen bij het maken van uw eigen uitbreidingsmethoden .
Bereik en herhaal
De statische methoden Range
en Repeat
op Enumerable
kunnen worden gebruikt om eenvoudige reeksen te genereren.
reeks
Enumerable.Range()
genereert een reeks gehele getallen met een startwaarde en een telling.
// Generate a collection containing the numbers 1-100 ([1, 2, 3, ..., 98, 99, 100])
var range = Enumerable.Range(1,100);
Herhaling
Enumerable.Repeat()
genereert een reeks herhalende elementen met een element en het vereiste aantal herhalingen.
// Generate a collection containing "a", three times (["a","a","a"])
var repeatedValues = Enumerable.Repeat("a", 3);
Overslaan en nemen
De Skip-methode retourneert een verzameling exclusief een aantal items vanaf het begin van de bronverzameling. Het aantal uitgesloten items is het nummer dat als argument wordt gegeven. Als de verzameling minder items bevat dan is opgegeven in het argument, wordt een lege verzameling geretourneerd.
De methode Take retourneert een verzameling met een aantal elementen vanaf het begin van de bronverzameling. Het aantal opgenomen items is het nummer dat als argument wordt gegeven. Als er minder items in de collectie zijn dan opgegeven in het argument, bevat de geretourneerde collectie dezelfde elementen als de broncollectie.
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
Skip en Take worden vaak samen gebruikt om resultaten te pagineren, bijvoorbeeld:
IEnumerable<T> GetPage<T>(IEnumerable<T> collection, int pageNumber, int resultsPerPage) {
int startIndex = (pageNumber - 1) * resultsPerPage;
return collection.Skip(startIndex).Take(resultsPerPage);
}
Waarschuwing: LINQ to Entities ondersteunt alleen Overslaan bij bestelde zoekopdrachten . Als u Skip probeert te gebruiken zonder te bestellen, krijgt u een NotSupportedException met het bericht 'De methode' Skip 'wordt alleen ondersteund voor gesorteerde invoer in LINQ naar entiteiten. De methode' OrderBy 'moet worden aangeroepen vóór de methode' Skip '. "
Ten eerste, FirstOrDefault, Last, LastOrDefault, Single en SingleOrDefault
Alle zes methoden retourneren een enkele waarde van het sequentietype en kunnen worden opgeroepen met of zonder een predicaat.
Afhankelijk van het aantal elementen dat overeenkomt met het predicate
of, als er geen predicate
is opgegeven, het aantal elementen in de bronreeks, gedragen ze zich als volgt:
Eerste()
- Retourneert het eerste element van een reeks of het eerste element dat overeenkomt met het opgegeven
predicate
. - Als de reeks geen elementen bevat, wordt een
InvalidOperationException
gegenereerd met het bericht: "Reeks bevat geen elementen". - Als de reeks geen elementen bevat die overeenkomen met het opgegeven
predicate
, wordt eenInvalidOperationException
gegenereerd met het bericht "Reeks bevat geen overeenkomend element".
Voorbeeld
// 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();
FirstOrDefault ()
- Retourneert het eerste element van een reeks of het eerste element dat overeenkomt met het opgegeven
predicate
. - Als de reeks geen elementen bevat, of geen elementen die overeenkomen met het opgegeven
predicate
, retourneert de standaardwaarde van het reekstype metdefault(T)
.
Voorbeeld
// 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();
Laatste()
- Retourneert het laatste element van een reeks of het laatste element dat overeenkomt met het opgegeven
predicate
. - Als de reeks geen elementen bevat, wordt een
InvalidOperationException
gegenereerd met het bericht "Reeks bevat geen elementen." - Als de reeks geen elementen bevat die overeenkomen met het opgegeven
predicate
, wordt eenInvalidOperationException
gegenereerd met het bericht "Reeks bevat geen overeenkomend element".
Voorbeeld
// 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 ()
- Retourneert het laatste element van een reeks, of het laatste element dat overeenkomt met het opgegeven
predicate
. - Als de reeks geen elementen bevat, of geen elementen die overeenkomen met het opgegeven
predicate
, retourneert de standaardwaarde van het reekstype metdefault(T)
.
Voorbeeld
// 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();
Single ()
- Als de reeks precies één element bevat, of precies één element dat overeenkomt met het opgegeven
predicate
, wordt dat element geretourneerd. - Als de reeks geen elementen bevat of geen elementen die overeenkomen met het opgegeven
predicate
, wordt eenInvalidOperationException
gegenereerd met het bericht "Reeks bevat geen elementen". - Als de reeks meer dan één element bevat, of meer dan één element dat overeenkomt met het opgegeven
predicate
, wordt eenInvalidOperationException
gegenereerd met het bericht "Reeks bevat meer dan één element". - Opmerking: om te evalueren of de reeks precies één element bevat, moeten maximaal twee elementen worden opgesomd.
Voorbeeld
// 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 ()
- Als de reeks precies één element bevat, of precies één element dat overeenkomt met het opgegeven
predicate
, wordt dat element geretourneerd. - Als de reeks geen elementen bevat of geen elementen die overeenkomen met het opgegeven
predicate
, wordtdefault(T)
geretourneerd. - Als de reeks meer dan één element bevat, of meer dan één element dat overeenkomt met het opgegeven
predicate
, wordt eenInvalidOperationException
gegenereerd met het bericht "Reeks bevat meer dan één element". - Als de reeks geen elementen bevat die overeenkomen met het opgegeven
predicate
, retourneert de standaardwaarde van het reekstype metdefault(T)
. - Opmerking: om te evalueren of de reeks precies één element bevat, moeten maximaal twee elementen worden opgesomd.
Voorbeeld
// 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();
aanbevelingen
Hoewel u
FirstOrDefault
,LastOrDefault
ofSingleOrDefault
kunt gebruiken om te controleren of een reeks items bevat, zijnAny
ofCount
betrouwbaarder. Dit komt omdat een retourwaarde vandefault(T)
van een van deze drie methoden niet bewijst dat de reeks leeg is, omdat de waarde van het eerste / laatste / enkele element van de reeks evengoeddefault(T)
Bepaal welke methoden het meest geschikt zijn voor het doel van uw code. Gebruik bijvoorbeeld
Single
alleen als u ervoor moet zorgen dat er een item in de collectie is dat overeenkomt met uw predikaat - gebruik andersFirst
; alsSingle
gooi een uitzondering als de reeks meer dan één overeenkomend element heeft. Dit geldt natuurlijk ook voor de "* OrDefault" -tegenpartijen.Met betrekking tot efficiëntie: hoewel het vaak aangewezen is om ervoor te zorgen dat er slechts één item (
Single
) of, of slechts één of nul (SingleOrDefault
) items is, geretourneerd door een query, vereisen beide methoden meer, en vaak de hele, van de collectie worden onderzocht om er zeker van te zijn dat er geen tweede overeenkomst is met de zoekopdracht. Dit is anders dan het gedrag van bijvoorbeeld deFirst
methode, waaraan kan worden voldaan nadat de eerste match is gevonden.
Behalve
De methode Except retourneert de set items die zich in de eerste verzameling bevinden, maar niet in de tweede. De standaard IEqualityComparer
wordt gebruikt om de items binnen de twee sets te vergelijken. Er is een overbelasting die een IEqualityComparer
als argument accepteert.
Voorbeeld:
int[] first = { 1, 2, 3, 4 };
int[] second = { 0, 2, 3, 5 };
IEnumerable<int> inFirstButNotInSecond = first.Except(second);
// inFirstButNotInSecond = { 1, 4 }
Output:
1
4
In dit geval .Except(second)
elementen uit de second
array uit, namelijk 2 en 3 (0 en 5 zijn niet opgenomen in de first
array en worden overgeslagen).
Merk op dat Except
impliciet Distinct
betekent (dwz het verwijdert herhaalde elementen). Bijvoorbeeld:
int[] third = { 1, 1, 1, 2, 3, 4 };
IEnumerable<int> inThirdButNotInSecond = third.Except(second);
// inThirdButNotInSecond = { 1, 4 }
Output:
1
4
In dit geval worden de elementen 1 en 4 slechts één keer geretourneerd.
Door IEquatable
implementeren of de functie een IEqualityComparer
, kan een andere methode worden gebruikt om de elementen te vergelijken. Merk op dat de GetHashCode
methode ook moet worden genegeerd, zodat deze een identieke hash-code retourneert voor object
die identiek zijn volgens de IEquatable
implementatie.
Voorbeeld met 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));
}
}
Output:
Chanoeka
SelectMany: Een reeks reeksen afvlakken
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 }
Gebruik SelectMany()
als u dat hebt, of u maakt een reeks reeksen, maar u wilt het resultaat als één lange reeks.
In LINQ Query Syntax:
var sequence = from subSequence in sequenceOfSequences
from item in subSequence
select item;
Als u een verzameling collecties heeft en tegelijkertijd aan gegevens uit de collectie van ouders en kinderen wilt kunnen werken, is dit ook mogelijk met SelectMany
.
Laten we eenvoudige klassen definiëren
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; }
}
Laten we aannemen dat we de volgende verzameling hebben.
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"
}
}
}
};
Nu willen we reacties Content
selecteren samen met Id
van BlogPost
die aan deze reactie is gekoppeld. Om dit te doen, kunnen we de juiste SelectMany
overbelasting gebruiken.
var commentsWithIds = posts.SelectMany(p => p.Comments, (post, comment) => new { PostId = post.Id, CommentContent = comment.Content });
Onze commentsWithIds
Id's zien er zo uit
{
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"
}
SelectMany
De SelectMany linq-methode 'vlakt' een IEnumerable<IEnumerable<T>>
in een IEnumerable<T>
. Alle T-elementen binnen de IEnumerable
instanties in de bron IEnumerable
zullen worden gecombineerd tot een enkele IEnumerable
.
var words = new [] { "a,b,c", "d,e", "f" };
var splitAndCombine = words.SelectMany(x => x.Split(','));
// returns { "a", "b", "c", "d", "e", "f" }
Als u een selectorfunctie gebruikt die invoerelementen omzet in reeksen, worden de elementen van die reeksen één voor één geretourneerd.
Merk op dat, in tegenstelling tot Select()
, het aantal elementen in de uitvoer niet hetzelfde hoeft te zijn als in de invoer.
Meer real-world voorbeeld
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);
}
Output:
Bob
vijzel
Jim
John
Allemaal
All
wordt gebruikt om te controleren of alle elementen van een verzameling overeenkomen met een voorwaarde of niet.
zie ook: Je verder
1. Lege parameter
Alles : mag niet worden gebruikt met lege parameter.
2. Lambda-expressie als parameter
All : Retourneert true
als alle elementen van de verzameling voldoen aan de lambda-expressie en anders false
:
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. Collectie leegmaken
All : Retourneert true
als de verzameling leeg is en een lambda-expressie is opgegeven:
var numbers = new List<int>();
bool result = numbers.All(i => i >= 0); // true
Opmerking: All
stoppen de iteratie van de verzameling zodra een element wordt gevonden dat niet overeenkomt met de voorwaarde. Dit betekent dat de verzameling niet noodzakelijk volledig zal worden opgesomd; het wordt alleen ver genoeg opgesomd om het eerste item te vinden dat niet overeenkomt met de voorwaarde.
Zoekopdrachtverzameling op type / cast-elementen om te typen
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 };
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
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
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
Unie
Voegt twee collecties samen om een aparte collectie te maken met behulp van de standaardvergelijkingsvergelijker
int[] numbers1 = { 1, 2, 3 };
int[] numbers2 = { 2, 3, 4, 5 };
var allElement = numbers1.Union(numbers2); // AllElement now contains 1,2,3,4,5
DOET MEE
Joins worden gebruikt om verschillende lijsten of tabellen met gegevens via een gemeenschappelijke sleutel te combineren.
Net als in SQL worden de volgende soorten Joins ondersteund in LINQ:
Binnen, links, rechts, kruis en volledige buitenste verbindingen.
De volgende twee lijsten worden in de onderstaande voorbeelden gebruikt:
var first = new List<string>(){ "a","b","c"}; // Left data
var second = new List<string>(){ "a", "c", "d"}; // Right data
(Inner) Doe mee
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"}
Linker buitenvoeg
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"}
Volledige deelname aan de buitenwereld
var fullOuterjoin = leftOuterJoin.Union(rightOuterJoin);
// Result: {"a","a"}
// {"b", null}
// {"c","c"}
// {null,"d"}
Praktisch voorbeeld
De bovenstaande voorbeelden hebben een eenvoudige gegevensstructuur, zodat u zich kunt concentreren op het technisch begrijpen van de verschillende LINQ-joins, maar in de echte wereld zou u tabellen hebben met kolommen waaraan u moet deelnemen.
In het volgende voorbeeld wordt er slechts één klasse Region
gebruikt, in werkelijkheid zou u twee of meer verschillende tabellen samenvoegen die dezelfde sleutel bevatten (in dit voorbeeld worden first
en second
verbonden via de gemeenschappelijke sleutel- ID
).
Voorbeeld: overweeg de volgende gegevensstructuur:
public class Region
{
public Int32 ID;
public string RegionDescription;
public Region(Int32 pRegionID, string pRegionDescription=null)
{
ID = pRegionID; RegionDescription = pRegionDescription;
}
}
Bereid nu de gegevens voor (dwz vul met gegevens):
// 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")
};
Je kunt zien dat in dit voorbeeld first
geen regiobeschrijvingen bevat, dus je wilt vanaf het second
. Dan ziet de binnenste verbinding eruit:
// 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"}
Dit resultaat heeft ter plekke anonieme objecten gemaakt, wat prima is, maar we hebben al een juiste klasse gemaakt - dus we kunnen deze opgeven: in plaats van select new { f.ID, s.RegionDescription };
we kunnen zeggen select new Region(f.ID, s.RegionDescription);
, die dezelfde gegevens retourneert, maar objecten van het type Region
- die compatibel blijven met de andere objecten.
onderscheiden
Retourneert unieke waarden uit een IEnumerable
. Uniciteit wordt bepaald met behulp van de standaardvergelijkingsvergelijker.
int[] array = { 1, 2, 3, 4, 2, 5, 3, 1, 2 };
var distinct = array.Distinct();
// distinct = { 1, 2, 3, 4, 5 }
Om een aangepast gegevenstype te vergelijken, moeten we de IEquatable<T>
-interface implementeren en GetHashCode
en Equals
methoden voor het type bieden. Of de gelijkheidsvergelijker kan worden opgeheven:
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);
Groep Door één of meerdere velden
Laten we aannemen dat we een filmmodel hebben:
public class Film {
public string Title { get; set; }
public string Category { get; set; }
public int Year { get; set; }
}
Groeperen op categorie:
foreach (var grp in films.GroupBy(f => f.Category)) {
var groupCategory = grp.Key;
var numberOfFilmsInCategory = grp.Count();
}
Groeperen op categorie en jaar:
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();
}
Bereik gebruiken met verschillende Linq-methoden
U kunt de klasse Enumerable naast Linq-query's gebruiken om lussen om te zetten in Linq one-liners.
Selecteer Voorbeeld
In tegenstelling tot dit:
var asciiCharacters = new List<char>();
for (var x = 0; x < 256; x++)
{
asciiCharacters.Add((char)x);
}
Je kan dit doen:
var asciiCharacters = Enumerable.Range(0, 256).Select(a => (char) a);
Waar bijvoorbeeld
In dit voorbeeld worden 100 nummers gegenereerd en even nummers worden geëxtraheerd
var evenNumbers = Enumerable.Range(1, 100).Where(a => a % 2 == 0);
Zoekopdracht bestellen - OrderBy () ThenBy () OrderByDescending () ThenByDescending ()
string[] names= { "mark", "steve", "adam" };
oplopend:
Zoekopdrachtsyntaxis
var sortedNames =
from name in names
orderby name
select name;
Methode Syntaxis
var sortedNames = names.OrderBy(name => name);
sortNames bevat de namen in de volgende volgorde: "adam", "mark", "steve"
Aflopend:
Zoekopdrachtsyntaxis
var sortedNames =
from name in names
orderby name descending
select name;
Methode Syntaxis
var sortedNames = names.OrderByDescending(name => name);
sortNames bevat de namen in de volgende volgorde: "steve", "mark", "adam"
Sorteer op verschillende velden
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}
};
Zoekopdrachtsyntaxis
var sortedPeople = from person in people
orderby person.LastName, person.FirstName, person.Age descending
select person;
Methode Syntaxis
sortedPeople = people.OrderBy(person => person.LastName)
.ThenBy(person => person.FirstName)
.ThenByDescending(person => person.Age);
Resultaat
1. Adam Ackerman 29
2. Adam Ackerman 15
3. Phil Collins 28
4. Steve Collins 30
Basics
LINQ is grotendeels nuttig voor het doorzoeken van collecties (of arrays).
Bijvoorbeeld, gegeven de volgende voorbeeldgegevens:
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 }
}
We kunnen deze gegevens "opvragen" met de LINQ-syntaxis. Om bijvoorbeeld alle studenten op te halen die vandaag een snack hebben:
var studentsWithSnacks = from s in classroom.Students
where s.HasSnack
select s;
Of om studenten met een cijfer van 90 of hoger op te halen en alleen hun namen te retourneren, niet het volledige Student
object:
var topStudentNames = from s in classroom.Students
where s.Grade >= 90
select s.Name;
De LINQ-functie bestaat uit twee syntaxis die dezelfde functies uitvoeren, bijna identieke prestaties hebben, maar heel anders zijn geschreven. De syntaxis in het bovenstaande voorbeeld wordt querysyntaxis genoemd . Het volgende voorbeeld illustreert de syntaxis van de methode . Dezelfde gegevens worden geretourneerd als in het bovenstaande voorbeeld, maar de manier waarop de query is geschreven, is anders.
var topStudentNames = classroom.Students
.Where(s => s.Grade >= 90)
.Select(s => s.Name);
GroupBy
GroupBy is een gemakkelijke manier om een IEnumerable<T>
verzameling items in verschillende groepen te sorteren.
Eenvoudig voorbeeld
In dit eerste voorbeeld eindigen we met twee groepen, oneven en even items.
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)
}
}
Complexer voorbeeld
Laten we een groep mensen op leeftijd als voorbeeld nemen. Eerst maken we een Person-object met twee eigenschappen, Naam en Leeftijd.
public class Person
{
public int Age {get; set;}
public string Name {get; set;}
}
Vervolgens maken we onze voorbeeldlijst van mensen met verschillende namen en leeftijden.
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"});
Vervolgens maken we een LINQ-query om onze lijst met mensen te groeperen op leeftijd.
var query = people.GroupBy(x => x.Age);
Als we dit doen, kunnen we de leeftijd voor elke groep bekijken en een lijst van elke persoon in de groep hebben.
foreach(var result in query)
{
Console.WriteLine(result.Key);
foreach(var person in result)
Console.WriteLine(person.Name);
}
Dit resulteert in de volgende uitvoer:
20
Mouse
30
Neo
Trinity
40
Morpheus
Dozer
Smith
Je kunt spelen met de live demo op .NET Fiddle
Ieder
Any
wordt gebruikt om te controleren of een element van een verzameling aan een voorwaarde voldoet of niet.
zie ook: .All , Any en FirstOrDefault: best practice
1. Lege parameter
Willekeurig : geeft true
als de verzameling elementen bevat en false
als de verzameling leeg is:
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. Lambda-expressie als parameter
Willekeurig : geeft true
als de verzameling een of meer elementen heeft die voldoen aan de voorwaarde in de lambda-expressie:
var arrayOfStrings = new string[] { "a", "b", "c" };
arrayOfStrings.Any(item => item == "a"); // true
arrayOfStrings.Any(item => item == "d"); // false
3. Collectie leegmaken
Any : Retourneert false
als de verzameling leeg is en een lambda-expressie is opgegeven:
var numbers = new List<int>();
bool result = numbers.Any(i => i >= 0); // false
Opmerking: Any
stopt de iteratie van de verzameling zodra deze een element vindt dat overeenkomt met de voorwaarde. Dit betekent dat de verzameling niet noodzakelijk volledig zal worden opgesomd; het wordt alleen ver genoeg opgesomd om het eerste item te vinden dat overeenkomt met de voorwaarde.
ToDictionary
De methode ToDictionary()
LINQ kan worden gebruikt om een Dictionary<TKey, TElement>
te genereren op basis van een gegeven IEnumerable<T>
Dictionary<TKey, TElement>
.
IEnumerable<User> users = GetUsers();
Dictionary<int, User> usersById = users.ToDictionary(x => x.Id);
In dit voorbeeld is het enige argument dat is doorgegeven aan ToDictionary
van het type Func<TSource, TKey>
, dat de sleutel voor elk element retourneert.
Dit is een beknopte manier om de volgende bewerking uit te voeren:
Dictionary<int, User> usersById = new Dictionary<int User>();
foreach (User u in users)
{
usersById.Add(u.Id, u);
}
U kunt ook een tweede parameter doorgeven aan de methode ToDictionary
, die van het type Func<TSource, TElement>
en de Value
retourneert die moet worden toegevoegd voor elk item.
IEnumerable<User> users = GetUsers();
Dictionary<int, string> userNamesById = users.ToDictionary(x => x.Id, x => x.Name);
Het is ook mogelijk om de IComparer
te geven die wordt gebruikt om sleutelwaarden te vergelijken. Dit kan handig zijn als de sleutel een string is en u wilt dat deze niet hoofdlettergevoelig is.
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
Opmerking: de ToDictionary
methode vereist dat alle sleutels uniek zijn, er mogen geen dubbele sleutels zijn. Als dit het geval is, wordt een uitzondering gegenereerd: ArgumentException: An item with the same key has already been added.
Als u een scenario hebt waarvan u weet dat u meerdere elementen met dezelfde sleutel hebt, kunt u beter in plaats daarvan ToLookup
gebruiken.
Aggregaat
Aggregate
Past een accumulatiefunctie toe op een reeks.
int[] intList = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int sum = intList.Aggregate((prevSum, current) => prevSum + current);
// sum = 55
- Bij de eerste stap
prevSum = 1
- Bij de tweede
prevSum = prevSum(at the first step) + 2
- Bij de i-de stap
prevSum = 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 !"
Een tweede overbelasting van Aggregate
ontvangt ook een seed
parameter die de initiële accumulatorwaarde is. Dit kan worden gebruikt om meerdere voorwaarden voor een verzameling te berekenen zonder deze meerdere keren te herhalen.
List<int> items = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
Voor de verzameling items
we willen berekenen
- Het totale
.Count
- Het aantal even getallen
- Verzamel elk vierde item
Met Aggregate
kan het als volgt worden gedaan:
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]
Merk op dat bij het gebruik van een anoniem type als seed elk item een nieuw object moet maken, omdat de eigenschappen alleen-lezen zijn. Met behulp van een aangepaste klasse kan men eenvoudig de informatie toewijzen en is er geen new
nodig (alleen bij het geven van de initiële seed
parameter
Een variabele in een Linq-query definiëren (laat trefwoord)
Om een variabele in een linq-uitdrukking te definiëren, kunt u het sleutelwoord let gebruiken . Dit wordt meestal gedaan om de resultaten van tussentijdse subquery's op te slaan, bijvoorbeeld:
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));
}
Output:
Het gemiddelde van de getallen is 4,5.
Zoekopdrachtresultaat omvat nummer 3 met vierkant van 9.
Zoekopdrachtresultaat omvat nummer 4 met vierkant van 16.
Zoekopdrachtresultaat omvat nummer 5 met vierkant van 25.
Zoekopdrachtresultaat bevat nummer 6 met vierkant van 36.
Zoekopdrachtresultaat omvat nummer 7 met vierkant van 49.
Zoekopdrachtresultaat omvat nummer 8 met vierkant van 64.
Zoekopdrachtresultaat omvat nummer 9 met vierkant van 81.
SkipWhile
SkipWhile()
wordt gebruikt om elementen uit te sluiten tot de eerste niet-overeenkomst (dit kan voor de meeste mensen intuïtief zijn)
int[] list = { 42, 42, 6, 6, 6, 42 };
var result = list.SkipWhile(i => i == 42);
// Result: 6, 6, 6, 42
DefaultIfEmpty
DefaultIfEmpty wordt gebruikt om een standaardelement te retourneren als de reeks geen elementen bevat. Dit element kan de standaardwaarde van het type zijn of een door de gebruiker gedefinieerd exemplaar van dat type. Voorbeeld:
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;
Gebruik in Left Joins :
Met DefaultIfEmpty
de traditionele Linq Join een standaardobject retourneren als er geen overeenkomst is gevonden. Dus fungeert als een SQL's Left Join. Voorbeeld:
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
In het geval dat een DefaultIfEmpty
wordt gebruikt (zonder een standaardwaarde op te geven) en dat zal resulteren in geen overeenkomende items in de juiste volgorde, moet u ervoor zorgen dat het object niet null
voordat u toegang krijgt tot de eigenschappen. Anders zal het resulteren in een NullReferenceException
. Voorbeeld:
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
wordt gebruikt om twee IEnumerable<T>
-reeksen met elkaar te vergelijken.
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);
Count en LongCount
Count
retourneert het aantal elementen in een IEnumerable<T>
. Count
toont ook een optionele predicaatparameter waarmee u de elementen kunt filteren die u wilt tellen.
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
werkt op dezelfde manier als Count
maar heeft een return-type long
en wordt gebruikt voor het tellen van int.MaxValue
IEnumerable<T>
int.MaxValue
die langer zijn dan 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
Stapsgewijs een zoekopdracht maken
Omdat LINQ uitgestelde uitvoering gebruikt , kunnen we een queryobject hebben dat de waarden niet bevat, maar de waarden retourneert wanneer deze worden geëvalueerd. We kunnen de query dus dynamisch bouwen op basis van onze controlestroom en deze evalueren zodra we klaar zijn:
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
});
We kunnen voorwaardelijk filters toepassen:
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);
}
}
We kunnen een sorteervolgorde toevoegen aan de query op basis van een voorwaarde:
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.
}
Onze zoekopdracht kan worden gedefinieerd om te beginnen vanaf een bepaald punt:
query = query.Skip(start - 1);
en gedefinieerd om een specifiek aantal records te retourneren:
if (count > -1) {
query = query.Take(count);
}
return query;
}
Zodra we het queryobject hebben, kunnen we de resultaten evalueren met een foreach
lus of een van de LINQ-methoden die een reeks waarden retourneert, zoals ToList
of ToArray
:
SearchModel sm;
// populate the search model here
// ...
List<VehicleModel> list = BuildQuery(5, sm).ToList();
ritssluiting
De Zip
uitbreidingsmethode werkt op twee collecties. Het koppelt elk element in de twee reeksen samen op basis van positie. Met een Func
instantie gebruiken we Zip
om elementen uit de twee C # -collecties paarsgewijs te verwerken. Als de reeks in grootte verschilt, worden de extra elementen van de grotere reeks genegeerd.
Om een voorbeeld te nemen uit het boek "C # in een notendop",
int[] numbers = { 3, 5, 7 };
string[] words = { "three", "five", "seven", "ignored" };
IEnumerable<string> zip = numbers.Zip(words, (n, w) => n + "=" + w);
Output:
3 = drie
5 = vijf
7 = zeven
GroupJoin met variabele buitenbereik
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 en ElementAtOrDefault
ElementAt
retourneert het item op index n
. Als n
niet binnen het bereik van de opsomming valt, werpt een ArgumentOutOfRangeException
.
int[] numbers = { 1, 2, 3, 4, 5 };
numbers.ElementAt(2); // 3
numbers.ElementAt(10); // throws ArgumentOutOfRangeException
ElementAtOrDefault
retourneert het item op index n
. Als n
niet binnen het bereik van de opsomming valt, wordt een default(T)
geretourneerd.
int[] numbers = { 1, 2, 3, 4, 5 };
numbers.ElementAtOrDefault(2); // 3
numbers.ElementAtOrDefault(10); // 0 = default(int)
Zowel ElementAt
als ElementAtOrDefault
zijn geoptimaliseerd voor wanneer de bron een IList<T>
en in die gevallen wordt normale indexering gebruikt.
Merk op dat voor ElementAt
, als de verstrekte index groter is dan de grootte van de IList<T>
, de lijst (maar technisch niet gegarandeerd) een ArgumentOutOfRangeException
moet IList<T>
.
Linq-kwantificatoren
Kwantificeerbewerkingen retourneren een Booleaanse waarde als sommige of alle elementen in een reeks aan een voorwaarde voldoen. In dit artikel zullen we enkele veel voorkomende LINQ naar objecten-scenario's zien waar we deze operators kunnen gebruiken. Er zijn 3 Quantifiers-bewerkingen die in LINQ kunnen worden gebruikt:
All
- wordt gebruikt om te bepalen of alle elementen in een reeks aan een voorwaarde voldoen. bv:
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
- wordt gebruikt om te bepalen of elementen in een reeks aan een voorwaarde voldoen. bv:
int[] query=new int[] { 2, 3, 4 }
query.Any (n => n == 3);
Contains
- wordt gebruikt om te bepalen of een reeks een opgegeven element bevat. bv:
//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");
Samenvoegen van meerdere reeksen
Beschouw entiteiten Customer
, Purchase
en PurchaseItem
als volgt:
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; }
}
Overweeg de volgende voorbeeldgegevens voor bovenstaande entiteiten:
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"
}
};
Overweeg nu onderstaande linq-query:
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
};
Om het resultaat van bovenstaande zoekopdracht uit te voeren:
foreach(var resultItem in result)
{
Console.WriteLine($"{resultItem.Name}, {resultItem.Description}, {resultItem.Detail}");
}
De uitvoer van de zoekopdracht zou zijn:
Klant1, Klant1-Aankoop1, Aankoop1-Aankoopartikel1
Klant1, Klant1-Aankoop2, Aankoop2-Aankoopartikel1
Klant1, Klant1-Aankoop2, Aankoop2-Aankoopartikel2
Klant2, Klant2-Aankoop2, Aankoop3-Aankoopartikel1
Deelnemen aan meerdere toetsen
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
};
Houd er rekening mee dat anonieme typen in bovenstaande join
dezelfde eigenschappen moeten bevatten, omdat objecten alleen als gelijk worden beschouwd als al hun eigenschappen gelijk zijn. Anders wordt de zoekopdracht niet gecompileerd.
Selecteer met Func selector - Gebruik deze om een rangorde van elementen te krijgen
Een van de overbelastingen van de Select
uitbreidingsmethoden geeft ook de index
van het huidige item in de collectie die wordt select
. Dit zijn enkele toepassingen.
Krijg het "rijnummer" van de items
var rowNumbers = collection.OrderBy(item => item.Property1)
.ThenBy(item => item.Property2)
.ThenByDescending(item => item.Property3)
.Select((item, index) => new { Item = item, RowNumber = index })
.ToList();
Krijg de rang van een item binnen zijn groep
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();
Krijg de rangorde van groepen (ook bekend in Oracle als 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();
Om dit te testen kunt u gebruiken:
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);
}
}
En gegevens:
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
retourneert elementen uit een reeks zolang de voorwaarde waar is
int[] list = { 1, 10, 40, 50, 44, 70, 4 };
var result = list.TakeWhile(item => item < 50).ToList();
// result = { 1, 10, 40 }
Som
De uitbreidingsmethode Enumerable.Sum
berekent de som van numerieke waarden.
Als de elementen van de collectie zelf getallen zijn, kunt u de som direct berekenen.
int[] numbers = new int[] { 1, 4, 6 };
Console.WriteLine( numbers.Sum() ); //outputs 11
Als het type elementen een complex type is, kunt u een lambda-expressie gebruiken om de waarde op te geven die moet worden berekend:
var totalMonthlySalary = employees.Sum( employee => employee.MonthlySalary );
De somuitbreidingsmethode kan met de volgende typen berekenen:
- Int32
- Int64
- single
- Dubbele
- Decimale
In het geval dat uw verzameling nulbare typen bevat, kunt u de operator null-coalescing gebruiken om een standaardwaarde voor null-elementen in te stellen:
int?[] numbers = new int?[] { 1, null, 6 };
Console.WriteLine( numbers.Sum( number => number ?? 0 ) ); //outputs 7
Opkijken
ToLookup retourneert een gegevensstructuur die indexering mogelijk maakt. Het is een uitbreidingsmethode. Het produceert een ILookup-instantie die kan worden geïndexeerd of geteld met behulp van een foreach-lus. De vermeldingen worden bij elke toets samengevoegd in groepen. - 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
Een ander voorbeeld:
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
Bouw je eigen Linq-operators voor IEnumerable
Een van de geweldige dingen van Linq is dat het zo gemakkelijk is uit te breiden. U hoeft alleen een extensiemethode te maken waarvan het argument 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;
}
}
}
In dit voorbeeld worden de items in een IEnumerable<T>
in lijsten met een vaste grootte, waarbij de laatste lijst de rest van de items bevat. Merk op hoe het object waarop het toestel wordt toegepast wordt doorgegeven (argument source
) als beginargument met het this
zoekwoord. Vervolgens wordt het yield
trefwoord gebruikt om het volgende item in de output IEnumerable<T>
alvorens vanaf dat punt verder te gaan met uitvoeren (zie yield-trefwoord ).
Dit voorbeeld zou als volgt in uw code worden gebruikt:
//using MyNamespace;
var items = new List<int> { 2, 3, 4, 5, 6 };
foreach (List<int> sublist in items.Batch(3))
{
// do something
}
In de eerste lus zou de sublijst {2, 3, 4}
en in de tweede {5, 6}
.
Aangepaste LinQ-methoden kunnen ook worden gecombineerd met standaard LinQ-methoden. bijvoorbeeld:
//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
Deze zoekopdracht retourneert even nummers gegroepeerd in batches met een grootte van 3: {0, 2, 4}, {6, 8, 10}, {12}
Vergeet niet dat using MyNamespace;
nodig using MyNamespace;
regel om toegang te krijgen tot de uitbreidingsmethode.
SelectMany gebruiken in plaats van geneste lussen
Gegeven 2 lijsten
var list1 = new List<string> { "a", "b", "c" };
var list2 = new List<string> { "1", "2", "3", "4" };
als u alle permutaties wilt uitvoeren, kunt u geneste lussen gebruiken, zoals
var result = new List<string>();
foreach (var s1 in list1)
foreach (var s2 in list2)
result.Add($"{s1}{s2}");
Met SelectMany kunt u dezelfde bewerking uitvoeren als
var result = list1.SelectMany(x => list2.Select(y => $"{x}{y}", x, y)).ToList();
Any and First (OrDefault) - beste praktijk
Ik zal niet uitleggen wat Any
en FirstOrDefault
doet omdat er al twee goede voorbeelden over zijn. Zie Any en First, FirstOrDefault, Last, LastOrDefault, Single en SingleOrDefault voor meer informatie.
Een patroon dat ik vaak in code zie en dat moet worden vermeden, is
if (myEnumerable.Any(t=>t.Foo == "Bob"))
{
var myFoo = myEnumerable.First(t=>t.Foo == "Bob");
//Do stuff
}
Het zou zo efficiënter kunnen worden geschreven
var myFoo = myEnumerable.FirstOrDefault(t=>t.Foo == "Bob");
if (myFoo != null)
{
//Do stuff
}
Aan de hand van het tweede voorbeeld wordt de verzameling slechts één keer doorzocht en geeft hetzelfde resultaat als de eerste. Hetzelfde idee kan worden toegepast op Single
.
GroupBy Sum and Count
Laten we een voorbeeldklasse nemen:
public class Transaction
{
public string Category { get; set; }
public DateTime Date { get; set; }
public decimal Amount { get; set; }
}
Laten we nu een lijst met transacties bekijken:
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) },
};
Als u de categoriegewijze som van het bedrag en het aantal wilt berekenen, kunt u GroupBy als volgt gebruiken:
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}"));
Als alternatief kunt u dit in één stap doen:
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}"));
De uitvoer voor beide bovenstaande zoekopdrachten zou hetzelfde zijn:
Categorie: Saving Account, Bedrag: 66, Aantal: 2
Categorie: Creditcard, Bedrag: 71, Aantal: 2
Categorie: Zichtrekening, Bedrag: 100, Aantal: 1
Omgekeerde
- Keert de volgorde van de elementen in een reeks om.
- Als er geen items zijn, wordt een
ArgumentNullException: source is null.
Voorbeeld:
// 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);
Onthoud dat Reverse()
kan werken, afhankelijk van de volgorde van uw LINQ-instructies.
//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
Reverse () werkt door alles te bufferen en er dan achteruit doorheen te lopen, wat niet erg efficiënt is, maar OrderBy ook niet vanuit dat perspectief.
In LINQ naar objecten zijn er bufferbewerkingen (Reverse, OrderBy, GroupBy, enz.) En niet-bufferende bewerkingen (Where, Take, Skip, enz.).
Voorbeeld: niet-buffering Extensie verlengen
public static IEnumerable<T> Reverse<T>(this IList<T> list) {
for (int i = list.Count - 1; i >= 0; i--)
yield return list[i];
}
Deze methode kan problemen tegenkomen als u de lijst muteert tijdens het itereren.
De opsomming opsommen
De IEnumerable <T> -interface is de basisinterface voor alle generieke enumerators en is een essentieel onderdeel van het begrijpen van LINQ. In de kern vertegenwoordigt het de volgorde.
Deze onderliggende interface wordt overgenomen door alle generieke collecties, zoals Collection <T> , Array , List <T> , Dictionary <TKey, TValue> Class en HashSet <T> .
Naast het vertegenwoordigen van de reeks, moet elke klasse die erft van IEnumerable <T> een IEnumerator <T> leveren. De teller noemt de iterator voor het opsombare, en deze twee onderling verbonden interfaces en ideeën vormen de bron van het gezegde "het opsombare opsommen".
"Het opsommen van de opsomming" is een belangrijke zin. Het opsombare is eenvoudig een structuur voor het itereren, het bevat geen gematerialiseerde objecten. Bij het sorteren kan bijvoorbeeld een opsomming de criteria bevatten van het veld om te sorteren, maar het gebruik van .OrderBy()
op zichzelf .OrderBy()
een IEnumerable <T> op die alleen weet hoe te sorteren. Het gebruik van een aanroep die de objecten materialiseert, zoals bij het herhalen van de set, staat bekend als opsommen (bijvoorbeeld .ToList()
). Het opsommingsproces gebruikt de opsomming van hoe om door de reeks te bladeren en de relevante objecten te retourneren (in volgorde, gefilterd, geprojecteerd, enz.).
Pas als de opsomming is opgesomd, veroorzaakt dit de materialisatie van de objecten, dat is wanneer metrieken als tijdcomplexiteit (hoe lang dit moet duren in verband met de reeksgrootte) en ruimtelijke complexiteit (hoeveel ruimte het moet gebruiken in verband met de reeksgrootte) worden gemeten.
Het maken van uw eigen klasse die erft van IEnumerable <T> kan een beetje ingewikkeld zijn, afhankelijk van de onderliggende reeks die moet worden opgesomd. Over het algemeen is het het beste om een van de bestaande generieke collecties te gebruiken. Dat gezegd hebbende, is het ook mogelijk om van de IEnumerable <T> interface te erven zonder een gedefinieerde array als onderliggende structuur te hebben.
Gebruik bijvoorbeeld de Fibonacci-serie als onderliggende reeks. Merk op dat de aanroep van Where
eenvoudigweg een IEnumerable
bouwt, en het is pas nadat een aanroeping tot opsomming is gedaan dat elk van de waarden wordt gematerialiseerd.
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;
}
}
}
uitgang
Enumerable built
Enumerating the Enumerable
4052739537881
Enumerating the Enumerable
4052739537881
Enumerable built
Enumerating the Enumerable
14930352
De kracht in de tweede set (de fibMod612) is dat hoewel we de oproep hebben gedaan om onze hele set Fibonacci-nummers te bestellen, omdat slechts één waarde is gebruikt met. .First()
de tijdcomplexiteit O (n) als slechts 1 waarde moest worden vergeleken tijdens de uitvoering van het bestelalgoritme. Dit komt omdat onze teller slechts om 1 waarde vroeg en dus niet de hele opsomming behoefde te worden uitgevoerd. Als we .Take(5)
hadden gebruikt in plaats van. .First()
, zou de teller 5 waarden hebben gevraagd en zouden er maximaal 5 waarden moeten worden gematerialiseerd. Vergeleken met de noodzaak om een hele set te bestellen en vervolgens de eerste 5 waarden te nemen, bespaart het principe van veel uitvoeringstijd en -ruimte.
OrderBy
Hiermee bestelt u een collectie met een opgegeven waarde.
Wanneer de waarde een geheel getal , dubbel of zwevend is, begint deze met de minimumwaarde , wat betekent dat u eerst de negatieve waarden krijgt, dan nul en daarna de positieve waarden (zie Voorbeeld 1).
Wanneer u bestelt met een teken , vergelijkt de methode de ascii-waarden van de tekens om de verzameling te sorteren (zie Voorbeeld 2).
Wanneer u tekenreeksen sorteert, vergelijkt de OrderBy-methode ze door hun CultureInfo te bekijken, maar normaal beginnende met de eerste letter in het alfabet (a, b, c ...).
Dit soort volgorde wordt oplopend genoemd, als je het andersom wilt, moet je afdalen (zie OrderByDescending).
Voorbeeld 1:
int[] numbers = {2, 1, 0, -1, -2};
IEnumerable<int> ascending = numbers.OrderBy(x => x);
// returns {-2, -1, 0, 1, 2}
Voorbeeld 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', '{' }
Voorbeeld:
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
Hiermee bestelt u een collectie met een opgegeven waarde.
Wanneer de waarde een geheel getal , dubbel of zwevend is, begint deze met de maximale waarde , wat betekent dat u eerst de positieve waarden krijgt, dan nul en daarna de negatieve waarden (zie Voorbeeld 1).
Wanneer u bestelt met een teken , vergelijkt de methode de ascii-waarden van de tekens om de verzameling te sorteren (zie Voorbeeld 2).
Wanneer u tekenreeksen sorteert, vergelijkt de OrderBy-methode ze door hun CultureInfo te bekijken, maar normaal beginnende met de laatste letter in het alfabet (z, y, x, ...).
Dit soort volgorde wordt aflopend genoemd, als je het andersom wilt, moet je oplopend zijn (zie OrderBy).
Voorbeeld 1:
int[] numbers = {-2, -1, 0, 1, 2};
IEnumerable<int> descending = numbers.OrderByDescending(x => x);
// returns {2, 1, 0, -1, -2}
Voorbeeld 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', '+', '!', ' ' }
Voorbeeld 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
Voegt twee collecties samen (zonder duplicaten te verwijderen)
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
bevat
MSDN:
Bepaalt of een reeks een opgegeven element bevat met behulp van een opgegeven
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
Een door de gebruiker gedefinieerd object gebruiken:
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
Gebruik van de Enumerable.Contains(value, comparer)
overbelasting:
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
Een slim gebruik van Contains
zou zijn om meerdere if
clausules te vervangen voor een Contains
aanroep.
Dus in plaats van dit te doen:
if(status == 1 || status == 3 || status == 4)
{
//Do some business operation
}
else
{
//Do something else
}
Doe dit:
if(new int[] {1, 3, 4 }.Contains(status)
{
//Do some business operaion
}
else
{
//Do something else
}