{"id":1533,"date":"2009-10-07T17:50:00","date_gmt":"2009-10-07T17:50:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/vbteam\/2009\/10\/07\/getting-loopy-matt-gertz\/"},"modified":"2024-07-05T12:46:34","modified_gmt":"2024-07-05T19:46:34","slug":"getting-loopy-matt-gertz","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/vbteam\/getting-loopy-matt-gertz\/","title":{"rendered":"Getting Loopy (Matt Gertz)"},"content":{"rendered":"<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">In my last post, I talked about the hidden costs that can occur whenever you call out to methods, particularly in loops.<span>&nbsp; <\/span>In looking at my examples, reader KG2V commented that another thing that folks need to be aware of is avoiding the assumption that the world (or, in this case, a list) is a static thing.<span>&nbsp; <\/span>It&rsquo;s a good point and it deserves some attention, particularly given that different languages will react in different ways to change.<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">To illustrate this point, let&rsquo;s assume that we have a form with a ListBox on it, and that we also have a button on the form that, when clicked, runs the following VB code:<\/font><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>&#8216; Bad &#8212; don&#8217;t do this<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>Private<\/span> <span>Sub<\/span> Button1_Click(<span>ByVal<\/span> sender <span>As<\/span> System.<span>Object<\/span>, <span>ByVal<\/span> e <span>As<\/span> System.<span>EventArgs<\/span>) <span>Handles<\/span> Button1.Click<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>For<\/span> i <span>As<\/span> <span>Integer<\/span> = 0 <span>To<\/span> ListBox1.Items.Count &#8211; 1<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>ListBox1.Items.RemoveAt(i)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Next<\/span><\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>End<\/span> <span>Sub<\/span><\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">Ostensibly, we want to remove all of the items from the ListBox. <span>&nbsp;<\/span>(Yes, normally we&rsquo;d use Clear() to do that, but stay with me for a moment; <span>&nbsp;<\/span>I&rsquo;ll switch to more &ldquo;real&rdquo; cases in a few paragraphs.)<span>&nbsp; <\/span>But if we run this code, we&rsquo;ll get an exception.<span>&nbsp; <\/span>Why?<span>&nbsp; <\/span>Because whenever we remove an object, all of the indices of the objects after it will decrease by 1.<span>&nbsp; <\/span>Let&rsquo;s assume that there are three objects in the list &#8211; <span>&nbsp;<\/span>A, B, and C &ndash; so that Count=3:<\/font><\/p>\n<p class=\"MsoListParagraphCxSpFirst\"><span><span><font size=\"3\">&middot;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font size=\"3\" face=\"Calibri\">When i = 0, we&rsquo;ll remove the object at index 0, which is A.<span>&nbsp; <\/span>B now moves to index 0, and C now moves to index 1.<\/font><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span><font size=\"3\">&middot;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font size=\"3\" face=\"Calibri\">When i = 1, we&rsquo;ll remove the object at index 1, which is C.<span>&nbsp; <\/span>There is nothing after C, so nothing else changes.<\/font><\/p>\n<p class=\"MsoListParagraphCxSpLast\"><span><span><font size=\"3\">&middot;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font size=\"3\" face=\"Calibri\">When i =2, we&rsquo;ll remove the object at index 2, which is&hellip; nothing.<span>&nbsp; <\/span>There&rsquo;s only one object left, which is B, and it&rsquo;s at index 0.<span>&nbsp; <\/span>So, we get an out-of-bounds exception.<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">In VB (as was pointed out to me in the comments section of my last point), the value of Count is only assessed once, right when the loop is encountered, so we will always loop through (in this case) three times.<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">But is the lack of reevaluation of Count the only issue?<span>&nbsp; <\/span>Maybe not.<span>&nbsp; <\/span>Let&rsquo;s try the same method in C#, in which Count is evaluated on every iteration:<\/font><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>\/\/ Bad &#8212; don&#8217;t do this.<\/span><\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>private<\/span> <span>void<\/span> button1_Click(<span>object<\/span> sender, <span>EventArgs<\/span> e)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>{<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>int<\/span> i;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>for<\/span> (i = 0; i &lt; listBox1.Items.Count; i++)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>{<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>listBox1.Items.RemoveAt(i);<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>}<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>}<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoListParagraphCxSpFirst\"><span><span><font size=\"3\">&middot;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font size=\"3\" face=\"Calibri\">When i = 0, we&rsquo;ll remove the object at index 0, which is A.<span>&nbsp; <\/span>B now moves to index 0, and C now moves to index 1.<\/font><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span><font size=\"3\">o<\/font><span>&nbsp;&nbsp; <\/span><\/span><\/span><font size=\"3\" face=\"Calibri\">Now, we&rsquo;ll loop back to the top.<span>&nbsp; <\/span>The value of i becomes 1, and the value of Count becomes 2.<span>&nbsp; <\/span>The stop condition is not met, so we keep going.<\/font><\/p>\n<p class=\"MsoListParagraphCxSpMiddle\"><span><span><font size=\"3\">&middot;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font size=\"3\" face=\"Calibri\">When i = 1, we&rsquo;ll remove the object at index 1, which is C.<span>&nbsp; <\/span>There is nothing after C, so nothing else changes.<\/font><\/p>\n<p class=\"MsoListParagraphCxSpLast\"><span><span><font size=\"3\">o<\/font><span>&nbsp;&nbsp; <\/span><\/span><\/span><font size=\"3\" face=\"Calibri\">Again, we loop back to the top.<span>&nbsp; <\/span>The value of i becomes 2, and the value of Count becomes 1.<span>&nbsp; <\/span>The stop condition is met, and we exit the loop.<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">So, although no exception is thrown, the method also fails in C# &#8212; B is left behind.<span>&nbsp; <\/span>So, the problem isn&rsquo;t whether or not Count is evaluated; it&rsquo;s that the elements in the list are changing indices on each deletion.<\/font><\/p>\n<h2><font color=\"#4f81bd\" size=\"4\" face=\"Cambria\">Sometimes doing things backwards is good<\/font><\/h2>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">This is why, in the case where multiple removals need to be made on an indexed list, a smart programmer (all of whom are yawning right now as they read this) will work backwards.<span>&nbsp; <\/span>That is, they start with the largest index and work down towards zero. <span>&nbsp;<\/span>For example, imagine that we want to remove the 4<sup>th<\/sup>, 7<sup>th<\/sup>, and 9<sup>th<\/sup> element from a list.<span>&nbsp; <\/span>We don&rsquo;t want to do it this way:<\/font><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>&#8216; Bad &#8212; don&#8217;t do this<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span>ListBox1.Items.RemoveAt(4)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span>ListBox1.Items.RemoveAt(7)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span>ListBox1.Items.RemoveAt(9)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"\nMsoNormal\"><font size=\"3\" face=\"Calibri\">Once we&rsquo;ve removed the 4<sup>th<\/sup> item, then the element at 7 will have moved to 6 and the element at 9 will have moved to 8.<span>&nbsp; <\/span>After we&rsquo;ve removed the new 7<sup>th<\/sup> item, the element at 8 will move to 7, and so on.<span>&nbsp; <\/span>We&rsquo;d either be removing the wrong elements or we&rsquo;d crash.<span>&nbsp; <\/span>It&rsquo;s far better to do them in reverse order:<\/font><\/p>\n<p class=\"MsoNoSpacing\"><span>ListBox1.Items.RemoveAt(9)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span>ListBox1.Items.RemoveAt(7)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span>ListBox1.Items.RemoveAt(4)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNoSpacing\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">None of the indices up front will be affected by changes that are occurring behind them in the list &ndash; their indices won&rsquo;t change, and this will work fine.<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">This issue comes up when you are (for example) deleting items from a listbox that supports multiselection.<span>&nbsp; <\/span>You basically have two ways to get the selected items in a listbox: you can use the SelectedIndices member function, which returns a collection of indices, or you can use SelectedItems, which returns a collection of selected items.<span>&nbsp; <\/span>Given either collection, there&rsquo;s no way to say &ldquo;remove all of these.&rdquo; There <b>is<\/b> a &ldquo;Remove&rdquo; command on the resulting collections, but if you use it, you&rsquo;ll just be removing the item from *that* collection and not from the ListBox, which has its own collection of items &ndash; essentially, you&rsquo;ll just deselect the items.<span>&nbsp; <\/span><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">So, what do you do?<span>&nbsp; <\/span>At first, it looks like you have two options; you could try removing them from the master list based on item, or based on object.<span>&nbsp; <\/span>Let&rsquo;s look at removal based on the item:<\/font><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>&#8216; Bad &#8212; don&#8217;t do this<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>For<\/span> <span>Each<\/span> index <span>As<\/span> <span>Integer<\/span> <span>In<\/span> ListBox1.SelectedIndices<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>ListBox1.Items.RemoveAt(index)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Next<\/span><\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">This won&rsquo;t work.<span>&nbsp; <\/span>The selected indices are in numerical order (no matter in which order you selected them), so you&rsquo;ll be sliding indices on each deletion and will subsequently delete the wrong things.<span>&nbsp; <\/span>You won&rsquo;t crash if you overreach on an index, because the For&hellip;Each loop will check for Nothing when it does a MoveNext internally and will exit gracefully, but this is actually worse since it may take longer before you notice a problem!<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">This won&rsquo;t work either, and will potentially crash:<\/font><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>&#8216; Bad &#8212; don&#8217;t do this<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>For<\/span> index <span>As<\/span> <span>Integer<\/span> = 0 <span>To<\/span> ListBox1.SelectedIndices.Count &#8211; 1<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>ListBox1.Items.RemoveAt(ListBox1.SelectedIndices(index))<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Next<\/span><\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\">Again, the indices for the downstream objects will decrease, you&rsquo;ll delete the wrong things, and you&rsquo;ll likely crash.<span>&nbsp; <\/span><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">You can, however, iterate down instead, as long as you are confident that the selected indices are in numerical order (as they will be in this case):<\/font><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>&#8216; This works:<\/span><\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>For<\/span> index <span>As<\/span> <span>Integer<\/span> = ListBox1.SelectedIndices.Count &#8211; 1 <span>To<\/span> 0 <span>Step<\/span> -1<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>ListBox1.Items.RemoveAt(ListBox1.SelectedIndices(index))<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Next<\/span><\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">Having found something that works via iteration, let&rsquo;s now consider the enumeration angle instead.<span>&nbsp; <\/span>In general, I prefer to enumerate lists whenever possible instead of iterating through them.<span>&nbsp; <\/span>This is not one of those times, though. <span>&nbsp;<\/span>Consider this code, which will fail:<\/font><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>&#8216; Bad &#8212; don&#8217;t do this<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>For<\/span> <span>Each<\/span> item <span>In<\/span> ListBox1.SelectedItems<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>ListBox1.Items.Remove(item)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Next<\/span><\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">&ldquo;Well, c&rsquo;mon, why doesn&rsquo;t <i>this<\/i> work?&rdquo; I hear you cry.<span>&nbsp; <\/span>&ldquo;I don&rsquo;t care about indices in this case, I&rsquo;m not even using them; I&rsquo;m just removing specific items.&rdquo;<span>&nbsp; <\/span>True, but if you run this code, you will indeed get an exception essentially stating that the collection has been modified and the list of selected items is no longer valid for enumeration.<span>&nbsp; <\/span>We can&rsquo;t work around this by assigning the collection of selected items to another variable and enumerating on that, since that just creates a reference to the same collection.<span>&nbsp; <\/span>In order for it to work, you&rsquo;ve have to get the references out of the collection itself so that the ListBox is no longer part of the equation on the removal loop&rsquo;s assessment.<\/font><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>&#8216; Really, this is just silly &#8212; don&#8217;t ever do this<\/span><\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Dim<\/span> selItems <span>As<\/span> <span>New<\/span> <span>Collection<\/span><\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>For<\/span> <span>Each<\/span> item <span>In<\/span> ListBox1.SelectedItems<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>selItems.Add(item)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Next<\/span><\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>For<\/span> <span>Each<\/span> item <span>In<\/span> selItems<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>ListBox1.Items.Remove(item)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>Next<\/span><\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">Functionally, this will work; however, you shouldn&rsquo;t do it for two reasons:<\/font><\/p>\n<p class=\"MsoListParagraphCxSpFirst\"><span><span><font size=\"3\">&middot;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font size=\"3\" face=\"Calibri\">First of all, it&rsquo;s stupid &ndash; you have to loop through twice.<span>&nbsp; <\/span>Occasionally, I do need to clone lists, like when creating an editable list from a read-only list &ndash; I&rsquo;ve done that in a previous blogpost on this site, in fact.<span>&nbsp; <\/span><span>&nbsp;<\/span>But my motives are not so noble here; I&rsquo;m using this for no other reason than allowing enumeration to work.<\/font><\/p>\n<p class=\"MsoListParagraphCxSpLast\"><span><span><font size=\"3\">&middot;<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font size=\"3\" face=\"Calibri\">Second, each call to RemoveItem has a hidden cost &ndash; namely, the list has got to go find that item.<span>&nbsp; <\/span>In this case, you can bet that there&rsquo;s good hashing going on that doesn&rsquo;t make that as painful as it might be, but do you really want to count on that, given the better numeric alternative above?<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">No, just there&rsquo;s really no good way to do removal by enumeration that I&rsquo;ve ever found.<span>&nbsp; <\/span>And since each object is totally clueless about whether it itself is selected or not, you can&rsquo;t even enumerate through the collection yourself and delete it if it&rsquo;s marked as selected.<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">Of course, I&rsquo;ve just talked about deletions, since they are more interesting than additions due to the crash potential.<span>&nbsp; <\/span>However, additions can also mess you up in a loop due to similar index changes, and I&rsquo;ve actually run into cases where a list may have both insertions and deletions done, so that I wasn&rsquo;t even able to leverage the change in list size as a clue to figure out what had happened to the data.<span>&nbsp; <\/span>The bottom line is, whenever doing an operation within a loop, you need to understand whether or not that operation will collide with your loop conditions and assumptions.<span>&nbsp; <\/span>Generally, you control this in your code, but in multithreaded environments, where changes to an existing store of data might be made from a different thread &ldquo;simultaneously&rdquo; with your thread, you really need to understand how thread-safe your code is with respect to data changes.<span>&nbsp; <\/span>(But that is a story for another time.)<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\" face=\"Calibri\">&lsquo;Til next time,<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\"><span>&nbsp; <\/span>&#8211;Ma<\/font><\/font><\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In my last post, I talked about the hidden costs that can occur whenever you call out to methods, particularly in loops.&nbsp; In looking at my examples, reader KG2V commented that another thing that folks need to be aware of is avoiding the assumption that the world (or, in this case, a list) is a [&hellip;]<\/p>\n","protected":false},"author":258,"featured_media":8818,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[22,195],"tags":[101,165,166,167],"class_list":["post-1533","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-matt-gertz","category-visual-basic","tag-matt-gertz","tag-vb2005","tag-vb2008","tag-vb2010"],"acf":[],"blog_post_summary":"<p>In my last post, I talked about the hidden costs that can occur whenever you call out to methods, particularly in loops.&nbsp; In looking at my examples, reader KG2V commented that another thing that folks need to be aware of is avoiding the assumption that the world (or, in this case, a list) is a [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/posts\/1533","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/users\/258"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/comments?post=1533"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/posts\/1533\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/media\/8818"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/media?parent=1533"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/categories?post=1533"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/tags?post=1533"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}