84 votes

pourquoi utiliser IList ou de la Liste?

Je sais qu'il y a eu beaucoup de posts sur ce sujet, mais il confond encore moi pourquoi devriez-vous passer dans une interface comme IList et retourner une interface comme IList dos plutôt que sur le béton de la liste.

J'ai lu beaucoup de posts pour dire à quel point cela rend plus facile de changer la mise en œuvre plus tard, mais je n'ai pas du bien voir comment cela fonctionne.

Dire que si j'ai cette méthode

  public class SomeClass
    {
        public bool IsChecked { get; set; }
    }

 public void LogAllChecked(IList<SomeClass> someClasses)
    {
        foreach (var s in someClasses)
        {
            if (s.IsChecked)
            {
                // log 
            }

        }
    }

Je ne suis pas sûr de savoir comment, dans l'avenir, grâce à des IList va m'aider.

Comment savoir si je suis déjà dans la méthode? Dois-je continuer à l'aide de IList?

public void LogAllChecked(IList<SomeClass> someClasses)
    {
        //why not List<string> myStrings = new List<string>()
        IList<string> myStrings = new List<string>();

        foreach (var s in someClasses)
        {
            if (s.IsChecked)
            {
                myStrings.Add(s.IsChecked.ToString());
            }

        }
    }

Que dois-je obtenir de l'aide IList maintenant?

public IList<int> onlySomeInts(IList<int> myInts)
    {
        IList<int> store = new List<int>();
        foreach (var i in myInts)
        {
            if (i % 2 == 0)
            {
                store.Add(i);
            }
        }

        return store;
    }

comment est-il maintenant? Est-il une nouvelle mise en œuvre d'une liste de int est que j'ai besoin de changement?

Donc en gros, j'ai besoin de voir quelques exemples de code de façon si je où à l'aide de IList aurait résolu un problème plus juste de prendre la Liste de tout.

De ma lecture, je pense que je pourrais avoir utilisé IEnumberable au lieu de IList depuis que je suis seulement en boucle à travers des choses.

Modifier Donc j'ai été jouer avec certains de mes méthodes sur la façon de le faire. Je suis toujours pas sûr de savoir le type de retour(si je doit le rendre plus concret ou une interface)

 public class CardFrmVm
    {
        public IList<TravelFeaturesVm> TravelFeaturesVm { get; set; }
        public IList<WarrantyFeaturesVm> WarrantyFeaturesVm { get; set; }

        public CardFrmVm()
        {
            WarrantyFeaturesVm = new List<WarrantyFeaturesVm>();
            TravelFeaturesVm = new List<TravelFeaturesVm>();
        }
}

 public class WarrantyFeaturesVm : AvailableFeatureVm
    {
    }

     public class TravelFeaturesVm : AvailableFeatureVm
    {

    }

      public class AvailableFeatureVm
    {
        public Guid FeatureId { get; set; }
        public bool HasFeature { get; set; }
        public string Name { get; set; }
    }


        private IList<AvailableFeature> FillAvailableFeatures(IEnumerable<AvailableFeatureVm> avaliableFeaturesVm)
        {
            List<AvailableFeature> availableFeatures = new List<AvailableFeature>();
            foreach (var f in avaliableFeaturesVm)
            {
                if (f.HasFeature)
                {
                                                    // nhibernate call to Load<>()
                    AvailableFeature availableFeature = featureService.LoadAvaliableFeatureById(f.FeatureId);
                    availableFeatures.Add(availableFeature);
                }
            }

            return availableFeatures;
        }

Donc, je suis maintenant de retour IList pour le simple fait que je vais l'ajouter à mon modèle de domaine de ce qui a une propriété comme cela

public virtual IList<AvailableFeature> AvailableFeatures { get; set; }

le ci-dessus est un IList lui-même que c'est ce qui semble être la norme à utiliser avec nhibernate. Sinon j'ai peut-être retourné IEnumberable en arrière, mais pas sûr. Ne peut toujours pas comprendre ce que l'utilisateur serait 100% besoin(c'est là que le retour de béton a un avantage sur).

Edit 2

Je pensais aussi qu'advient-il si je veux faire passer par référence dans ma méthode?

private void FillAvailableFeatures(IEnumerable<AvailableFeatureVm> avaliableFeaturesVm, IList<AvailableFeature> toFill)
            {

                foreach (var f in avaliableFeaturesVm)
                {
                    if (f.HasFeature)
                    {
                                                        // nhibernate call to Load<>()
                        AvailableFeature availableFeature = featureService.LoadAvaliableFeatureById(f.FeatureId);
                        toFill.Add(availableFeature);
                    }
                }
            }

aurais-je des problèmes avec ça? Depuis que pourraient-ils ne pas passer un tableau(qui a une taille fixe)? Serait-il mieux peut-être pour une Liste concrète?

161voto

Eric Lippert Points 300275

Il y a trois questions: quel type dois-je utiliser pour un paramètre formel? Que dois-je utiliser pour une variable locale? et que dois-je utiliser pour un type de retour?

Les paramètres formels:

Le principe ici est de ne pas demander plus que vous avez besoin. IEnumerable<T> communique "j'ai besoin d'obtenir les éléments de cette séquence du début à la fin". IList<T> communique "j'ai besoin d'obtenir et de définir les éléments de cette séquence dans un ordre arbitraire". List<T> communique "j'ai besoin d'obtenir et de définir les éléments de cette séquence dans un ordre arbitraire et je n'accepte que les listes; je n'accepte pas les tableaux."

En demandant plus que vous avez besoin, vous (1) faire de l'appelant de faire des travaux inutiles pour satisfaire vos demandes inutiles, et (2) de communiquer des mensonges pour le lecteur. Demander seulement pour ce que vous allez utiliser. De cette façon, si l'appelant a une séquence, ils n'ont pas besoin d'appeler ToList pour satisfaire votre demande.

Variables locales:

Utiliser ce que vous voulez. C'est votre méthode. Vous êtes le seul qui peut voir l'intérieur détails de mise en œuvre de la méthode.

Le type de retour:

Même principe que précédemment, renversée. Offre le strict minimum que votre interlocuteur a besoin. Si l'appelant ne requiert que la possibilité d'énumérer la séquence, que de leur donner un IEnumerable<T>.

34voto

Will Points 76760

La raison la plus pratique que j'ai jamais vu a été donné par Jeffrey Richter dans le CLR via C#.

Le modèle est de prendre le basest de la classe ou de l'interface possible pour vos arguments et retourner le plus spécifique de la classe ou de l'interface possible pour votre type de retour. Cela donne à vos visiteurs une plus grande souplesse en passant dans les types à vos méthodes et à la plupart des occasions de cast/réutiliser les valeurs de retour.

Par exemple, la méthode suivante

public void PrintTypes(IEnumerable items) 
{ 
    foreach(var item in items) 
        Console.WriteLine(item.GetType().FullName); 
}

permet la méthode à appeler en passant dans n'importe quel type qui peuvent être exprimées à une énumération. Si vous avez été plus précis

public void PrintTypes(List items)

alors, dites, si vous aviez un tableau et souhaitent imprimer leurs noms de type de la console, vous devez tout d'abord créer une nouvelle Liste et de le remplir avec vos types. Et, si vous avez utilisé une implémentation générique, vous ne être en mesure d'utiliser une méthode qui fonctionne pour n'importe quel objet qu'avec des objets d'un type spécifique.

Lorsque l'on parle de types de retour, le plus vous êtes précis, plus flexible, les appelants peuvent être avec elle.

public List<string> GetNames()

vous pouvez utiliser ce type de retour pour itérer les noms

foreach(var name in GetNames())

ou vous pouvez indexer directement dans la collection

Console.WriteLine(GetNames()[0])

Alors que, si vous étiez en récupérant un peu moins spécifique de type

public IEnumerable GetNames()

vous devez massage le type de retour pour obtenir la première valeur

Console.WriteLine(GetNames().OfType<string>().First());

14voto

Charles Lambert Points 3051

IEnumerable<T> vous permet d'itérer sur une collection. ICollection<T> s'appuie sur le présent et permet également d'ajouter et de supprimer des éléments. IList<T> permet l'accès et la modification d'un indice spécifique. En exposant celle que vous attendez de vos consommateurs pour travailler avec, vous êtes libre de modifier votre mise en œuvre. List<T> arrive à mettre en œuvre tous les trois de ces interfaces.

Si vous exposez votre propriété comme un List<T> ou même un IList<T> lorsque tout ce que vous voulez que vos consommateurs à avoir est la capacité de parcourir la collection. Ensuite, ils pourraient venir à dépendre sur le fait qu'ils peuvent modifier la liste. Puis, plus tard, si vous décidez de convertir les données réelles magasin à partir d'un List<T> d'un Dictionary<T,U> et exposer les clés de dictionnaire que la valeur réelle du bien (j'avais à faire exactement cela avant). Les consommateurs qui en sont venus à s'attendre à ce que leurs modifications seront prises en compte à l'intérieur de votre classe n'ont plus cette capacité. C'est un gros problème! Si vous exposez le List<T> comme IEnumerable<T> vous pouvez aisément prédire que votre collection n'est pas modifié à l'extérieur. C'est l'une des puissances d'exposant List<T> comme toutes les interfaces ci-dessus.

Ce niveau d'abstraction va dans l'autre direction lorsqu'il appartient à des paramètres de la méthode. Lorsque vous passez votre liste à une méthode qui accepte IEnumerable<T> vous pouvez être sûr que votre liste ne va pas être modifié. Lorsque vous êtes la personne la mise en œuvre de la méthode et vous dire que vous acceptez une IEnumerable<T> parce que tout ce que vous devez faire est de parcourir la liste. Ensuite, la personne qui appelle la méthode est appeler gratuitement avec n'importe quel type de données qui est énumérable. Cela permet à votre code pour être utilisé dans l'inattendu, mais parfaitement valide façons.

D'où il suit que votre méthode de la mise en œuvre de représenter les variables locales toutefois vous le souhaitez. Les détails d'implémentation ne sont pas exposés. Vous laissant libre de modifier votre code pour quelque chose de mieux sans affecter les personnes à l'appel de votre code.

Vous ne pouvez pas prédire l'avenir. En supposant qu'un type de propriété sera toujours bénéfique en tant que List<T> est immédiatement et limiter votre capacité à s'adapter à des attentes de votre code. Oui, vous pouvez ne jamais modifier le type de données à partir d'un List<T> mais vous pouvez être sûr que si vous avez à. Votre code est prêt pour cela.

10voto

Adrian Points 2320

Réponse Courte:

Vous passez l'interface de sorte que peu importe ce que la mise en œuvre concrète de cette interface que vous utilisez, votre code sera le soutenir.

Si vous utilisez une mise en œuvre concrète de la liste, une autre mise en œuvre de la même liste ne sera pas pris en charge par votre code.

Lire un peu sur l'héritage et le polymorphisme.

8voto

phoog Points 22667

Voici un exemple: j'avais un projet une fois où nos listes est devenu très grand, et résultant de la fragmentation du tas d'objets volumineux faisait mal de performance. Nous avons remplacé Liste avec LinkedList. LinkedList ne contient pas un tableau, tout d'un coup, nous n'avions presque pas utiliser des tas d'objets volumineux.

Surtout, nous avons utilisé les listes de la IEnumerable<T>, de toute façon, il n'y a aucun changement nécessaire. (Et oui, je vous recommande de déclarer les références que IEnumerable si tout ce que vous faites est de l'énumération.) Dans quelques endroits, nous avons besoin de la liste de l'indexeur, nous avons donc réalisé un inefficace IList<T> wrapper autour de la listes liées. Nous avons besoin de la liste de l'indexeur rarement, de sorte que l'inefficacité n'était pas un problème. Si elle l'avait été, nous pourrions avoir fourni quelques autres de la mise en œuvre de IList, peut-être qu'une collection de petites suffisamment de tableaux, qui aurait été plus efficace indexables tout en évitant les objets de grande taille.

En fin de compte, vous pourriez avoir besoin de remplacer une mise en œuvre pour une raison quelconque; la performance est juste une possibilité. Quelle que soit la raison, à l'aide de la moins-type dérivé du possible, permettra de réduire la nécessité pour les changements dans votre code lorsque vous modifier le type à l'exécution de vos objets.

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