int[] numbersA = { 0, 1, 2, 3 };
int[] numbersB = { 0, 2, 4, 6 };
// liste des numéros des deux tableauxIEnumerable<int> allNumbers = numbersA.Concat(numbersB); // 0 1 2 3 0 2 4 6// liste des numéros des deux tableaux sans les doublonsIEnumerable<int> uniqueNumbers = numbersA.Union(numbersB); // 0 1 2 3 4 6// liste des numéros présent dans les deux tableauxIEnumerable<int> duplicateNumbers = numbersA.Intersect(numbersB); // 0 2// équivalent Comprehension Query SyntaxIEnumerable<int> query = from itemA in numbersA
join itemB in numbersB on itemA equals itemB
selectitemA;
// liste des numéros présent dans numbersA mais pas dans numbersBIEnumerable<int> exceptNumbers = numbersA.Except(numbersB); // 1 3
// tri croissant suivant la longueur des motsstring[] words = { "cherry", "apple", "blueberry" };
varsortedWords = words.OrderBy(w => w.Length);
varsortedWords = from w in words
orderby w.Length
selectw;
// tri décroissantdouble[] doubles = { 1.7, 2.3, 1.9, 4.1, 2.9 };
varsortedDoubles = doubles.OrderByDescending(d => d);
varsortedDoubles = from d in doubles
orderby d descendingselectd;
// double tri suivant la taille des mots, et en cas d'égalité suivant l'ordre alphabétiquestring[] digits = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" };
varsortedDigits = digits.OrderBy(d => d.Length).ThenBy(d => d);
varsortedDigits = from d in digits
orderby d.Length, d
selectd;
// OrderBy extension method to sort by string property name and specifying the ascending or descending orderprivatestaticIEnumerable<T> OrderBy<T>(
thisIEnumerable<T> source,
stringpropertyName,
boolascendingOrder)
{
varpropertyDescriptor = TypeDescriptor.GetProperties(typeof(T))
.Find(propertyName, false);
return ascendingOrder ?
source.OrderBy(x => propertyDescriptor.GetValue(x)) :
source.OrderByDescending(x => propertyDescriptor.GetValue(x));
}
GroupBy
Permet de regrouper suivant une clé. Retourne une liste de IGrouping contenant chacun la valeur de la clé et la liste des éléments correspondant à la clé.
IGrouping<out TKey, out TElement> hérite de IEnumerable<TElement>.
// groupement des mots suivant leur première lettrestring[] words = { "blueberry", "chimpanzee", "abacus", "banana", "apple", "cheese" };
varwordGroups = from w in words
group w by w[0] intog;
varwordGroups = words.GroupBy(w => w[0]);
foreach (var g in wordGroups)
{
Console.WriteLine("Words that start with the letter '{0}':", g.Key);
// g implémente IEnumerable<string>foreach (var w in g)
{
Console.WriteLine(w);
}
}
// Result// Words that start with the letter 'b':// blueberry// banana// Words that start with the letter 'c':// chimpanzee// cheese// Words that start with the letter 'a':// abacus// apple// VariantevarwordGroups = from w in words
group w by w[0] into g
selectnew { FirstLetter = g.Key, Words = g };
varwordGroups = words.GroupBy(w => w[0])
.Select(g => new { FirstLetter = g.Key, Words = g });
foreach (var g in wordGroups)
{
Console.WriteLine("Words that start with the letter '{0}':", g.FirstLetter);
foreach (var w in g.Words)
{
Console.WriteLine(w);
}
}
// Group by sur plusieurs élémentsvar groupement= maListe.GroupBy(element => new {element.Propriété1, element.Propriété2});
Avec un delegate nommé
// avec un delagate anonymevarwordGroups = words.GroupBy(w => w[0]);
// avec un delegate nommévarwordGroups = words.GroupBy(GroupByDelegate);
privatecharGroupByDelegate(stringword)
{
return word[0];
// le type de retour est object dans le cas où return renvoie un type anonyme// return new { Prop1 = word[0], Prop2 = word[1] };
}
// IEnumerable<IGrouping<char, <string>> → IEnumerable<char, string>IEnumerable<char, string> result = words.GroupBy(w => w[0])
.SelectMany(x => x.Select(y => new { Letter = x.Key, Word = y }));
// { (b, blueberry), (b, banana), (c, chimpanzee), ... }
Lookup
Lookup est executé immediatement, à la différence de GroupBy qui est exécuté en différé. Il vaut mieux utiliser Lookup si l'on souhaite parcourir le résultat plus d'une fois. ILookup<TKey, TElement> hérite de IEnumerable<IGrouping<TKey, TElement>> et se comporte comme un IDictionary<TKey, IEnumerable<TElement>>
// groupement des mots suivant leur première lettrestring[] words = { "blueberry", "chimpanzee", "abacus", "banana", "apple", "cheese" };
// GroupByIEnumerable<IGrouping<char, string>> wordGroupsGroupBy = words.GroupBy(w => w[0]);
foreach (IGrouping<char, string> g in wordGroupsGroupBy)
{
Console.WriteLine("Words that start with the letter '{0}':", g.Key);
foreach (var w in g)
{
Console.WriteLine(w);
}
}
// LookupILookup<char, string> wordGroupsLookup = words.ToLookup(w => w[0]);
foreach (IGrouping<char, string> g in wordGroupsLookup)
{
Console.WriteLine("Words that start with the letter '{0}':", g.Key);
foreach (var w in g)
{
Console.WriteLine(w);
}
}
// Même résultats avec GroupBy et Lookup// Words that start with the letter 'b':// blueberry// banana// Words that start with the letter 'c':// chimpanzee// cheese// Words that start with the letter 'a':// abacus// apple// possibilité d'utiliser ILookup comme un dictionnaireforeach (string w in wordGroupsLookup['a'])
{
Console.WriteLine(w);
}
// Resultats// abacus// apple// filter a lookupvarfilteredWords = words
.SelectMany(
x => x.Where(y => y == "banana"), // filter out banana
(x, y) => new { x.Key, Value = y })
.ToLookup(x => x.Key, x => x.Value);
varfilteredWords = words
.Select(x =>
(
x.Key,
x.First() // keep only the first word
))
.ToDictionary(x => x.Key, x => x.Item2);
Chunk
// create 10 chunks from a list from 1 to 100: [1 .. 10], [11 .. 20], [21 .. 30]IEnumerable<int[]> chunks = Enumerable.Range(1, 100).Chunk(10);
Any – All
if the enumeration is empty, All returns true.
// Test si au moins un des éléments valide le critère.string[] words = { "pomme", "fraise", "kiwi", };
boolisWordWithW = words.Any(w => w.Contains("w")); // true// Test si tous les éléments valident le critère.int[] numbers = { 1, 11, 3, 19, 41, 65, 19 };
boolonlyOdd = numbers.All(n => n % 2 == 1); // true, tous impair// attention, si l'énumération est vide, All retourne truevarnumbers = Enumerable.Empty<int>();
boolonlyOdd = numbers.All(n => n % 2 == 1); // trueboolonlyOdd = numbers.DefaultIfEmpty().All(n => n % 2 == 1); // false// numbers.DefaultIfEmpty() retourne un enumerable avec un élément de valeur default, ici 0 pour int// numbers.DefaultIfEmpty(-1) on peut aussi forcer la valeur// Any permet aussi de tester si une énumération contient des éléments ou nonvarnumbers = newList<int> { 1, 2 };
boolhasElements = numbers.Any();
Distinct retourne un IEnumerable<T> sans doublons. La notion d'identique est vérifiée via Equals et GetHashCode.
Cette notion d'identique peut être étendue à une comparaison sur une propriété de l'objet (ex : objet.Nom).
Personne[] personnes = {
newPersonne() { Nom = "Nicolas" },
newPersonne() { Nom = "Audrey" },
newPersonne() { Nom = "Nicolas" }
};
personnes.Distinct(); // Nicolas Audrey Nicolas
personnes.DistinctBy(personne => personne.Nom); // Nicolas Audrey
Pour cela il faut ajouter une méthode d'extension à IEnumerable<T> :
stringsentence = "un deux trois quatre cinq";
string[] words = sentence.Split(' ');
stringreversed = words.Aggregate((workingSentence, next) => next + " " + workingSentence);
// premier passage workingSentence = un, next = deux, retour "deux un"// deuxième passage workingSentence = "deux un", next = trois, retour "trois deux un"// sum with seed of 0 in case of intArray is empty to avoid "sequence contains no elements" exceptionvarsum = intArray.Aggregate(0, (current, next) => current + next);
// concat array of strings into 1 string separated by new line
stringArray.Aggregate(newStringBuilder(), (current, next) => current.AppendLine(next)).ToString();
int[] chiffres = { 1, 2, 3, 4 };
// saute les 2 premiers éléments et retourne le reste
chiffres.Skip(2); // 3 4// prend seulement les 2 premiers éléments
chiffres.Take(2); // 1 2
sinon les surcharges de Equals et GetHashCode de l'objet sont utilisés
sinon il est possible de spécifier un EqualityComparer<T>
varproducts1 = newProduct[] { newProduct { Name = "apple" }, newProduct { Name = "orange" } };
varproducts2 = newProduct[] { newProduct { Name = "apple" }, newProduct { Name = "lemon" } };
products1.Intersect(products2); // empty// leur référence est utilisé pour les comparer, ils sont donc tous différents même s'ils ont le même Name
product1.Intersect(product2, newProductEqualityComparer()); // apple
publicclassProduct : IEquatable<Product>
{
publicstring Name { get; set; }
publicboolEquals(Productother)
{
if (ReferenceEquals(null, other)) returnfalse;
if (ReferenceEquals(this, other)) returntrue;
returnstring.Equals(Name, other.Name);
}
publicoverrideboolEquals(objectobj)
{
if (ReferenceEquals(null, obj)) returnfalse;
if (ReferenceEquals(this, obj)) returntrue;
if (obj.GetType() != this.GetType()) returnfalse;
returnEquals((Product) obj);
}
publicoverrideintGetHashCode()
{
return (Name != null ? Name.GetHashCode() : 0);
}
}
privatestaticasyncIAsyncEnumerable<string> GetKeysAsync()
{
varkeys = awaitGetKeysAsync();
// here we have some changes on the result of the async call before the result would be sent.varkeyGroups = keys.GroupBy(x => x.Last());
foreach (var keyGroup in keyGroups)
{
yieldreturn$"{keyGroup.Key} - {keyGroup}";
}
}
// to enumerate an IAsyncEnumerable<T>awaitforeach (var key inGetKeysAsync())
{ }
varkeys = awaitGetKeysAsync().ToListAsync();
Met en corrélation les éléments de deux séquences en fonction des clés qui correspondent.
// join a list of users and a list of groups// pair them arround the matching between user.GroupId and group.Idvarquery = users.Join(groups,
user => user.GroupId,
group => group.Id,
(user, group) =>
new
{
UserName = user.Name,
GroupName = group.Name
});
varquery = from u in users
join g in groups
on u.GroupId equals g.Id
selectnew
{
UserName = u.Name,
GroupName = g.Name
};
DataTableclients = dataSet.Tables["client"];
DataTablecommandes = dataSet.Tables["commande"];
var query =
from client in clients.AsEnumerable()
join commande in commandes.AsEnumerable()
on client.Field<int>("id") equals commande.Field<int>("clientId")
where client.Field<string>("name").EndsWith("2")
&& commande.Field<string>("details").EndsWith("2")
selectnew
{
ClientId = client.Field<int>("id"),
ClientName = client.Field<string>("name"),
CommandeId = commande.Field<int>("id"),
CommandeDetails = commande.Field<string>("details")
};
Affichage de la requête dans WPF
<DataGridItemsSource="{Binding Result}" />
privateIEnumerable_result;
public IEnumerable Result
{
get
{
return_result;
}
set
{
_result = value;
OnPropertyChanged("Result");
}
}
/* ... */
Result = query;
Provider: query provider associé au type de données (LINQ to SQL)
Expression: arbre d'expression
ElementType: type d'élément retourné
Query examples
// filtering: where and orfrom user in context.Users
where user.Age > 30 && (user.Name == "Nicolas" || user.Name == "Guillaume")
selectuser;
// orderingfrom user in context.Users
orderby user.Name ascending, user.Age descendingselectuser;
// grouping: IEnumerable<IGrouping<int, User>>from user in context.Users
group user by user.Age into g
order by g.Key
selectg;
// joiningfrom user in context.Users
joingroupin context.Groups
on user.GroupId equalsgroup.Id
selectnew { UserName = user.Name, GroupName = group.Name };
// use find if the condition is part of the primary keyvaritem = awaitthis.DbContext.Set<Item>().FindAsync(cancellationToken, id);
// return null if no item with the same id is found in the context or in the database
Misc
View → Server Explorer → Data Connections → Add Connection
Add → Class → Data → LINQ to SQL Classes (fichier *.dbml)
Glisser les tables dans le designer
vardb = newDataClasses1DataContext();
var query =
from client in db.clients
selectnew
{
ClientId = client.id,
ClientName = client.name
};
// affichage du résultat dans un dataGridView WinForm
dataGridView1.DataSource = query;
LINQ to XML
XDocument est disponible à partir du Framework 3.5
Ne pas fait directement les requêtes sur XDocument car c'est un nœud virtuel qui ne contient que le nœud racine. Utiliser donc XDocument.Root.
// chargement du XML depuis un fichierXDocumentxDoc = XDocument.Load(xmlFilePath);
// ou depuis une chaîne de caratèresXDocumentxDoc = XDocument.Parse("<?xml version=\"1.0\"?><root><noeud /></root>");
// Séléctionne tous les noeuds "noeud2" fils direct de "noeud1" // qui lui-même est fils direct du noeud racine.varquery = xDoc.Root.Element("noeud1").Elements("noeud2");
varquery = from n in xDoc.Root.Element("noeud1").Elements("noeud2")
select n;
// Séléctionne tous les noeuds "noeud2" fils direct ou non de la racinevarquery = xDoc.Root.Descendants("noeud2");
// Récupère le contenu de noeud1, <noeud1>true</noeud1> ici "true" au format stringstringcontent = xDoc.Root.Element("noeud1").Value;
// Convertionboolb = XmlConvert.ToBoolean(content);
foreach (var noeud in query) // noeud est un XElement
{
stringattribut = noeud.Attribute("attribut").Value;
stringattribut = (string)noeud.Attribute("attribut"); // cast de XAttribut
}