J'ai une collection de valeurs entières dans une collection List. Je veux appeler une fonction pour chaque valeur de la collection où l'un des arguments de la fonction est une valeur de collection. Sans faire cela dans une boucle foreach ... existe-t-il un moyen d'accomplir cela avec une expression lambda/linq?
quelque chose comme ... myList.Where(p => myFunc(p.Value));
?
merci d'avance, -s
LINQ n'aide pas ici, mais vous pouvez utiliser List<T>.ForEach
:
Liste <T> .ForEach, méthode
Effectue l'action spécifiée sur chaque élément de la liste.
Exemple:
myList.ForEach(p => myFunc(p));
La valeur transmise à l'expression lambda est l'élément de liste, donc dans cet exemple p
est un long
si myList
est un List<long>
.
Comme d'autres affiches l'ont noté, vous pouvez utiliser List<T>.ForEach
.
Cependant, vous pouvez facilement écrire une méthode d'extension qui vous permet d'utiliser ForEach sur n'importe quel IEnumerable<T>
public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
foreach(T item in source)
action(item);
}
Ce qui signifie que vous pouvez désormais:
myList.Where( ... ).ForEach( ... );
Japper! Vous pouvez utiliser List.ForEach
Mais le problème est que cela ne fonctionne que pour la liste (à coup sûr, vous pouvez tout changer en liste à la volée, mais moins de code plus amusant:)). De plus, vous devez utiliser la prédiction Action<>
Qui ne fournit aucun type de retour.
Quoi qu'il en soit, il y a une réponse à ce que vous vouliez, vous pouvez le rendre possible de la même manière que vous l'avez deviné avec Func<>
Mais avec une petite astuce comme ci-dessous:
Je ferai tout ici à la volée:
string[] items = (new string[] { "d", "f" }).
Select(x => new Func<string>(() => {
//Do something here...
Console.WriteLine(x);
return x.ToUpper();
}
)).Select(t => t.Invoke()).ToArray<string>();
Il y a quelques points ici:
First, j'ai défini un tableau de chaînes à la volée qui peut être remplacé par n'importe quelle collection qui prend en charge LINQ.
Second, À l'intérieur du Select
un Func<string>
Va être déclaré pour chaque élément du tableau donné comme il s'appelait x
.
( Regardez les parenthèses vides et rappelez-vous que lorsque vous définissez un Func<type**1**, type**2**, ..., type**n**>
, Le dernier type est toujours le type de retour, donc lorsque vous écrivez juste Func<string>
Cela indique une fonction qui n'a pas de paramètre d'entrée et son type de retour est une chaîne )
Il est clair que nous voulons utiliser la fonction sur le tableau donné (new string[] { "d", "f" })
, Et c'est pourquoi je définis une fonction sans paramètre et j'ai aussi utilisé la même variable x
à l'intérieur de Func<>
pour faire référence aux mêmes éléments du tableau.
Third, Si vous ne mettez pas la seconde Select alors vous avez une collection de deux fonctions qui retournent une chaîne, et déjà ils ont leurs entrées du premier tableau. Vous pouvez le faire et conserver le résultat dans une variable anonymous
, puis appeler leurs méthodes .Invoke
. Je l'écris ici:
var functions = (new string[] { "d", "f" }).
Select(x => new Func<string>(() => {
//Do something here...
Console.WriteLine(x);
return x.ToUpper();
}));
string[] items = functions.Select(t => t.Invoke()).ToArray<string>();
Enfin, j'ai converti le résultat en tableau! Vous avez maintenant un tableau de chaînes!
Désolé ça devient long !! J'espère que ce serait utile.
Vous pouvez utiliser la méthode List.ForEach, en tant que telle:
myList.ForEach(p => myFunc(p));
Non - si vous souhaitez appeler une fonction pour chaque élément d'une liste, vous devez appeler la fonction pour chaque élément de la liste.
Cependant, vous pouvez utiliser la méthode IList<T>.ForEach()
comme un peu de sucre syntaxique pour rendre la "fin commerciale" du code plus lisible, comme ceci:
items.ForEach(item => DoSomething(item));
Si vous ne souhaitez pas utiliser la méthode ForEach de List<T>
, mais utilisez plutôt IEnumerable<T>
(comme on le fait souvent quand "LINQ: ing"), je suggère MoreLINQ écrit par Jon Skeet, et al.
MoreLINQ vous donnera ForEach, et aussi Pipe (et un tas d'autres méthodes), qui effectue une action sur chaque élément, mais renvoie le même IEnumerable<T>
(par rapport à vide).
Voici un mini (mais complet) exemple.
public class Employee
{
public string EmployeeNumber { get; set; }
public DateTime? HireDate { get; set; }
}
public class EmployeeCollection : List<Employee>
{ }
private void RunTest()
{
EmployeeCollection empcoll = new EmployeeCollection();
empcoll.Add(new Employee() { EmployeeNumber = "1111", HireDate = DateTime.Now });
empcoll.Add(new Employee() { EmployeeNumber = "3333", HireDate = DateTime.Now });
empcoll.Add(new Employee() { EmployeeNumber = "2222", HireDate = null });
empcoll.Add(new Employee() { EmployeeNumber = "4444", HireDate = null });
//Here's the "money" line!
empcoll.Where(x => x.HireDate.HasValue == false).ToList().ForEach(item => ReportEmployeeWithMissingHireDate(item.EmployeeNumber));
}
private void ReportEmployeeWithMissingHireDate(string employeeNumber)
{
Console.WriteLine("We need to find a HireDate for '{0}'!", employeeNumber);
}