Disons que j'ai une classe
public class MyObject
{
public int SimpleInt{get;set;}
}
Et j'ai un List<MyObject>
, et je ToList()
le change , puis je change l'un des SimpleInt
, ma modification sera-t-elle propagée à la liste d'origine. En d'autres termes, quel serait le résultat de la méthode suivante?
public void RunChangeList()
{
var objs = new List<MyObject>(){new MyObject(){SimpleInt=0}};
var whatInt = ChangeToList(objs );
}
public int ChangeToList(List<MyObject> objects)
{
var objectList = objects.ToList();
objectList[0].SimpleInt=5;
return objects[0].SimpleInt;
}
Pourquoi?
P / S: Je suis désolé s'il semble évident de le découvrir. Mais je n'ai pas de compilateur avec moi maintenant ...
.ToList()
une copie superficielle . Les références sont copiées, mais les nouvelles références pointent toujours vers les mêmes instances que celles vers lesquelles pointent les références d'origine. Quand on y pense,ToList
on ne peut pas en créernew MyObject()
quandMyObject
est unclass
type.Réponses:
Oui,
ToList
créera une nouvelle liste, mais comme dans ce casMyObject
est un type de référence, la nouvelle liste contiendra des références aux mêmes objets que la liste d'origine.La mise à jour de la
SimpleInt
propriété d'un objet référencé dans la nouvelle liste affectera également l'objet équivalent dans la liste d'origine.(Si
MyObject
était déclaré comme unstruct
plutôt que comme un,class
alors la nouvelle liste contiendrait des copies des éléments de la liste d'origine, et la mise à jour d'une propriété d'un élément dans la nouvelle liste n'affecterait pas l'élément équivalent dans la liste d'origine.)la source
List
of structs, une affectation comme celle-objectList[0].SimpleInt=5
ci ne serait pas autorisée (erreur de compilation C #). C'est parce que la valeur de retour de l'get
accesseur de l'indexeur de liste n'est pas une variable (c'est une copie retournée d'une valeur struct), et donc la définition de son membre.SimpleInt
avec une expression d'affectation n'est pas autorisée (cela muterait une copie qui n'est pas conservée) . Eh bien, qui utilise de toute façon des structures mutables?foreach (var s in listOfStructs) { s.SimpleInt = 42; }
. Le truc vraiment méchant est lorsque vous essayez quelque chose commelistOfStructs.ForEach(s => s.SimpleInt = 42)
: le compilateur le permet et le code s'exécute sans exceptions, mais les structures de la liste resteront inchangées!De la source du réflecteur:
Alors oui, votre liste d'origine ne sera pas mise à jour (c'est-à-dire ajouts ou suppressions) mais les objets référencés le seront.
la source
ToList
créera toujours une nouvelle liste, qui ne reflètera aucune modification ultérieure de la collection.Cependant, il reflétera les modifications apportées aux objets eux-mêmes (à moins qu'ils ne soient des structures modifiables).
En d'autres termes, si vous remplacez un objet de la liste d'origine par un autre objet, le
ToList
contiendra toujours le premier objet.Cependant, si vous modifiez l'un des objets de la liste d'origine, le
ToList
contiendra toujours le même objet (modifié).la source
La réponse acceptée répond correctement à la question du PO sur la base de son exemple. Cependant, il ne s'applique que lorsqu'il
ToList
est appliqué à une collection de béton; il ne tient pas lorsque les éléments de la séquence source doivent encore être instanciés (en raison d'une exécution différée). Dans ce dernier cas, vous pourriez obtenir un nouvel ensemble d'éléments à chaque fois que vous appelezToList
(ou énumérez la séquence).Voici une adaptation du code de l'OP pour démontrer ce comportement:
Alors que le code ci-dessus peut sembler artificiel, ce comportement peut apparaître comme un bogue subtil dans d'autres scénarios. Voir mon autre exemple pour une situation dans laquelle des tâches sont générées à plusieurs reprises.
la source
Oui, cela crée une nouvelle liste. C'est par conception.
La liste contiendra les mêmes résultats que la séquence énumérable d'origine, mais matérialisés dans une collection persistante (en mémoire). Cela vous permet de consommer les résultats plusieurs fois sans encourir le coût de recalcul de la séquence.
La beauté des séquences LINQ est qu'elles sont composables. Souvent, ce que
IEnumerable<T>
vous obtenez est le résultat de la combinaison de plusieurs opérations de filtrage, de classement et / ou de projection. Les méthodes d'extension commeToList()
etToArray()
vous permettent de convertir la séquence calculée en une collection standard.la source
Une nouvelle liste est créée mais les éléments qu'elle contient sont des références aux éléments originaux (tout comme dans la liste d'origine). Les modifications apportées à la liste elle-même sont indépendantes, mais les modifications apportées aux éléments se trouvent dans les deux listes.
la source
Trébuchez simplement sur ce vieux poste et pensez à ajouter mes deux cents. Généralement, en cas de doute, j'utilise rapidement la méthode GetHashCode () sur n'importe quel objet pour vérifier les identités. Donc pour ci-dessus -
Et réponds sur ma machine -
Donc, essentiellement, l'objet que la liste porte reste le même dans le code ci-dessus. J'espère que l'approche aide.
la source
Je pense que cela équivaut à demander si ToList fait une copie profonde ou superficielle. Comme ToList n'a aucun moyen de cloner MyObject, il doit faire une copie superficielle, de sorte que la liste créée contient les mêmes références que l'original, le code renvoie donc 5.
la source
ToList
créera une toute nouvelle liste.Si les éléments de la liste sont des types valeur, ils seront directement mis à jour, s'il s'agit de types référence, toutes les modifications seront reflétées dans les objets référencés.
la source
Dans le cas où l'objet source est un vrai IEnumerable (c'est-à-dire pas simplement une collection empaquetée et comme énumérable), ToList () ne peut PAS retourner les mêmes références d'objet que dans le IEnumerable d'origine. Il renverra une nouvelle liste d'objets, mais ces objets peuvent ne pas être identiques ou même égaux aux objets générés par IEnumerable lorsqu'il est à nouveau énuméré
la source
Cela mettra également à jour l'objet d'origine. La nouvelle liste contiendra des références aux objets qu'elle contient, tout comme la liste d'origine. Vous pouvez modifier les éléments l'un ou l'autre et la mise à jour se reflétera dans l'autre.
Maintenant, si vous mettez à jour une liste (en ajoutant ou en supprimant un élément) qui ne sera pas reflétée dans l'autre liste.
la source
Je ne vois nulle part dans la documentation que ToList () est toujours garanti de renvoyer une nouvelle liste. Si un IEnumerable est une liste, il peut être plus efficace de vérifier cela et de renvoyer simplement la même liste.
Le souci est que parfois vous voudrez peut-être être absolument sûr que la liste renvoyée est! = À la liste d'origine. Étant donné que Microsoft ne documente pas que ToList renverra une nouvelle liste, nous ne pouvons pas être sûrs (à moins que quelqu'un ait trouvé cette documentation). Cela pourrait également changer à l'avenir, même si cela fonctionne maintenant.
new List (IEnumerable enumerablestuff) est garanti pour renvoyer une nouvelle List. J'utiliserais plutôt ceci.
la source
ToList
semble créer une nouvelle référence d'objet de liste lorsqu'il est appelé sur aList
, BenB a raison de dire que ce n'est pas garanti par la documentation MS.IEnumerable<>.ToList()
est en fait implémenté en tant quenew List<>(source)
et il n'y a pas de remplacement spécifique pourList<>
, doncList<>.ToList()
renvoie effectivement une nouvelle référence d'objet de liste. Mais encore une fois, selon la documentation MS, il n'y a aucune garantie que cela ne changera pas à l'avenir, bien que cela cassera probablement la plupart du code.