247 votes

Comment trouver le dernier élément d'une Liste<> ?

Le texte suivant est un extrait de mon code :

public class AllIntegerIDs 
{
    public AllIntegerIDs() 
    {            
        m_MessageID = 0;
        m_MessageType = 0;
        m_ClassID = 0;
        m_CategoryID = 0;
        m_MessageText = null;
    }

    ~AllIntegerIDs()
    {
    }

    public void SetIntegerValues (int messageID, int messagetype,
        int classID, int categoryID)
    {
        this.m_MessageID = messageID;
        this.m_MessageType = messagetype;
        this.m_ClassID = classID;
        this.m_CategoryID = categoryID;
    }

    public string m_MessageText;
    public int m_MessageID;
    public int m_MessageType;
    public int m_ClassID;
    public int m_CategoryID;
}

J'essaie d'utiliser le code suivant dans ma fonction main() :

List<AllIntegerIDs> integerList = new List<AllIntegerIDs>();

/* some code here that is ised for following assignments*/
{
   integerList.Add(new AllIntegerIDs());
   index++;
   integerList[index].m_MessageID = (int)IntegerIDsSubstring[IntOffset];
   integerList[index].m_MessageType = (int)IntegerIDsSubstring[IntOffset + 1];
   integerList[index].m_ClassID = (int)IntegerIDsSubstring[IntOffset + 2];
   integerList[index].m_CategoryID = (int)IntegerIDsSubstring[IntOffset + 3];
   integerList[index].m_MessageText = MessageTextSubstring;
}

Le problème est là : J'essaie d'imprimer tous les éléments de ma liste en utilisant une boucle for :

for (int cnt3 = 0 ; cnt3 <= integerList.FindLastIndex ; cnt3++) //<----PROBLEM HERE
{
   Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}\n", integerList[cnt3].m_MessageID,integerList[cnt3].m_MessageType,integerList[cnt3].m_ClassID,integerList[cnt3].m_CategoryID, integerList[cnt3].m_MessageText);
}

Je veux trouver le dernier élément afin d'égaliser cnt3 dans ma boucle for et d'imprimer toutes les entrées de la liste. Chaque élément de la liste est un objet de la classe AllIntegerIDs comme mentionné ci-dessus dans l'exemple de code. Comment puis-je trouver le dernier élément valide de la liste ?

Dois-je utiliser quelque chose comme integerList.Find(integerList[].m_MessageText == null ;

Si je l'utilise, il faudra un indice qui ira de 0 à un maximum quelconque. Cela signifie que je devrai utiliser une autre boucle for que je n'ai pas l'intention d'utiliser. Existe-t-il un moyen plus court/meilleur ?

Merci, Viren

0 votes

@Viren : J'ai indenté le code pour qu'il s'affiche correctement. Si vous avez fait des modifications sous ma responsabilité, pouvez-vous vous assurer que je ne les ai pas annulées ?

9 votes

Cela n'a rien à voir avec votre question, mais vous ne devriez vraiment pas implémenter un finaliseur à moins qu'il ne soit nécessaire.

0 votes

Cela n'a rien à voir avec la question, mais pour des raisons de lisibilité et de maintenabilité, je vous suggère de faire ce qui suit AllIntegerIDs newItem = new AllIntegerID(); utilisez-le pour affecter tous les champs et puis appelez integerList.Add(newItem) . Ou bien utilisez des propriétés plutôt que des champs et utilisez la syntaxe de l'initialisateur d'objet C# 3.0.

384voto

Pour obtenir le dernier élément d'une collection, utilisez LastOrDefault() et Dernier() méthodes d'extension

var lastItem = integerList.LastOrDefault();

OU

var lastItem = integerList.Last();

N'oubliez pas d'ajouter using System.Linq; ou cette méthode ne sera pas disponible.

27 votes

Oui, c'est la meilleure solution, Last et LastOrDefault sont optimisés pour les listes<>.

0 votes

@chillitom J'utilise Last() beaucoup, mais j'ai récemment commencé à me demander si elle traitait IList instances correctement. Où puis-je trouver des informations à ce sujet ?

4 votes

@Gusdor Je ne l'ai pas vu documenté mais j'ai tendance à me tourner vers les sources (ou à utiliser un désassembleur comme Resharper, dotPeek ou ILSpy) directement pour ces choses. De là, je peux voir que First , FirstOrDefault , Last , LastOrDefault , Single , SingleOrDefau‌​lt , ElementAt y ElementAtOrDefault sont optimisés pour IList<TSource> , Count y Contains sont optimisés pour ICollection<TSource> y Cast<TResult> est optimisé pour IEnumerable<TResult> .

271voto

Jared Points 3852

Si vous voulez seulement accéder au dernier élément de la liste, vous pouvez faire ce qui suit

var item = integerList[integerList.Count - 1];

Pour obtenir le nombre total d'éléments dans la liste, vous pouvez utiliser la propriété Count.

var itemCount = integerList.Count;

0 votes

C'est vrai, mais n'y a-t-il pas une façon interne de le faire ? Par exemple, j'ai rencontré une situation où j'ai dû utiliser un long cast sur une collection afin d'exposer une collection interne, ce qui rend mon code assez laid Checker checker = (pipes[from].ItemsSource as ObservableCollection<Checker>).ElementAt((pipes[from].ItemsSource as ObservableCollection<Checker>).Count - 1) ; des idées ?

20 votes

@Jared je pense que tu as oublié d'ajouter cette ligne "if(integerList.Count != 0)" avant la première ligne

27 votes

A mon avis, cette réponse ne mérite pas d'être la première réponse, elle se lit mal et laisse la possibilité d'une erreur si le compte est à zéro. Le site CleanCode™ L'approche la plus simple serait d'utiliser Last / LastOrDefault comme indiqué ci-dessous.

22voto

JFDev Points 51

Allons à la racine de la question, comment adresser le dernier élément d'une liste en toute sécurité ?

En supposant que

List<string> myList = new List<string>();

Puis

//NOT safe on an empty list!
string myString = myList[myList.Count -1];

//equivalent to the above line when Count is 0, bad index
string otherString = myList[-1];

"count-1" est une mauvaise habitude à moins que vous ne garantissiez d'abord que la liste n'est pas vide.

Il n'y a pas de moyen pratique de contourner la vérification de la liste vide, sauf à le faire.

Le moyen le plus court auquel je peux penser est

string myString = (myList.Count != 0) ? myList [ myList.Count-1 ] : "";

vous pourriez aller jusqu'au bout et créer un délégué qui renvoie toujours true, et le passer à FindLast, qui renverra la dernière valeur (ou la valeur construite par défaut si la liste est vide). Cette fonction commence à la fin de la liste et sera donc Big O(1) ou temps constant, bien que la méthode soit normalement O(n).

//somewhere in your codebase, a strange delegate is defined
private static bool alwaysTrue(string in)
{
    return true;
}

//Wherever you are working with the list
string myString = myList.FindLast(alwaysTrue);

La méthode FindLast est moche si l'on compte la partie déléguée, mais elle n'a besoin d'être déclarée qu'à un seul endroit. Si la liste est vide, elle retournera une valeur construite par défaut du type "" pour les chaînes de caractères. Prendre le délégué alwaysTrue un peu plus loin, en en faisant un modèle au lieu d'un type de chaîne, serait plus utile.

2 votes

Le délégué peut être remplacé par une expression lambda : myList.FindLast(_unused_variable_name => true); Cela fonctionnera quel que soit le type. Une version plus courte est myList.FindLast(_ => true); mais je trouve que le simple trait de soulignement (ou tout autre identifiant à un seul caractère) peut parfois prêter à confusion.

6voto

Eric J. Points 73338

Changement

for (int cnt3 = 0 ; cnt3 <= integerList.FindLastIndex ; cnt3++)

à

for (int cnt3 = 0 ; cnt3 < integerList.Count; cnt3++)

0 votes

Foreach est souvent plus pratique à utiliser, mais est légèrement plus lent.

0 votes

Si vous utilisez Count... faites un -1 ou vous aurez une erreur d'index. for (int cnt3 = 0 ; cnt3 < integerList.Count - 1 ; cnt3++)

4 votes

C'est pourquoi j'ai changé <= en <. Le code est correct tel que posté :-)

3voto

RiddlerDev Points 1893

Essayez d'utiliser un For Each au lieu d'un For pour les listes. Ce sera beaucoup plus facile.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X