Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • Home
  • SEARCH
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 8071403
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 5, 20262026-06-05T13:41:12+00:00 2026-06-05T13:41:12+00:00

I was reading the code for List<T>.InsertRange() in the .NET 4.0 framework, when I

  • 0

I was reading the code for List<T>.InsertRange() in the .NET 4.0 framework, when I noticed a strange peculiarity. Here is the code for reference:

    public void InsertRange(int index, IEnumerable<T> collection) {
        if (collection==null) { 
            ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
        } 

        if ((uint)index > (uint)_size) {
            ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index); 
        }
        Contract.EndContractBlock();

        ICollection<T> c = collection as ICollection<T>; 
        if( c != null ) {    // if collection is ICollection<T>
            int count = c.Count; 
            if (count > 0) { 
                EnsureCapacity(_size + count);
                if (index < _size) { 
                    Array.Copy(_items, index, _items, index + count, _size - index);
                }

                // If we're inserting a List into itself, we want to be able to deal with that. 
                if (this == c) {
                    // Copy first part of _items to insert location 
                    Array.Copy(_items, 0, _items, index, index); 
                    // Copy last part of _items back to inserted location
                    Array.Copy(_items, index+count, _items, index*2, _size-index); 
                }
                else {
                    T[] itemsToInsert = new T[count];
                    c.CopyTo(itemsToInsert, 0); 
                    itemsToInsert.CopyTo(_items, index);
                } 
                _size += count; 
            }
        } 
        else {
            using(IEnumerator<T> en = collection.GetEnumerator()) {
                while(en.MoveNext()) {
                    Insert(index++, en.Current); 
                }
            } 
        } 
        _version++;
    }

In particular, notice that _version is always incremented at the end of the function. This means that in-progress enumerations over the list will be invalidated at any non-exceptional call to InsertRange, even if the List was not changed. For instance, the following code throws:

static void Main(string [] args) {
    var list = new List<object>() {1, 2 };


    using(var enumerator = list.GetEnumerator()) {
    if(enumerator.MoveNext())
        Console.WriteLine(enumerator.Current);

    list.InsertRange(1, new object[]{});


    if(enumerator.MoveNext()) // ** InvalidOperationException
        Console.WriteLine(enumerator.Current);
    }
}

Modifying the method so that enumeration is not invalidated in this way would not increase execution time at all because the code already checks the size of count. It could be rewritten as follows:

public void InsertRange(int index, IEnumerable<T> collection) {
    if (collection==null) { 
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
    } 

    if ((uint)index > (uint)_size) {
        ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index); 
    }
    Contract.EndContractBlock();

    ICollection<T> c = collection as ICollection<T>; 
    if( c != null ) {    // if collection is ICollection<T>
        int count = c.Count; 
        if (count > 0) { 
            EnsureCapacity(_size + count);
            if (index < _size) { 
                Array.Copy(_items, index, _items, index + count, _size - index);
            }

            // If we're inserting a List into itself, we want to be able to deal with that. 
            if (this == c) {
                // Copy first part of _items to insert location 
                Array.Copy(_items, 0, _items, index, index); 
                // Copy last part of _items back to inserted location
                Array.Copy(_items, index+count, _items, index*2, _size-index); 
            }
            else {
                T[] itemsToInsert = new T[count];
                c.CopyTo(itemsToInsert, 0); 
                itemsToInsert.CopyTo(_items, index);
            } 
            _size += count;
            _version++;
        }
    } 
    else {
        var inserted = false;

        using(IEnumerator<T> en = collection.GetEnumerator()) {
            while(en.MoveNext()) {
                inserted = true;
                Insert(index++, en.Current); 
            }  
        }

        if (inserted) _version++; 
    } 
}

Which has as its only disadvantage an extra local variable (which will probably be JITed into a register), maybe 20 bytes increase in the working set, and an irrelevant amount of extra CPU work when inserting IEnumerables. If the extra bool or the in-loop assignment need to be avoided, the insertion for IEnumerables could be performed as

if(en.MoveNext()) {
    Insert(index++, en.Current); 
    _version++;
}

while(en.MoveNext()) {
    Insert(index++, en.Current); 
}  

So…

Is the .NET implementation the intended behavior, or is it a mistake?

EDIT:

I realize that if you are enumeration on one thread while modifying the thread on another, you are doing something wrong. According to the documentation, the behavior in these cases is undefined. However, List<T> does the programmer a favor and throws an exception in these cases. I am not asking if List<T> follows the documentation correctly: it does. I am asking if it is implemented in a way that is not what was intended by Microsoft.

If InsertRange() is behaving as intended, then List<T> has inconsistent behavior. The RemoveRange() method only invalidates enumeration if items were actually removed:

static void Main(string [] args) {
    var list = new List<object>() {1, 2 };

    using(var enumerator = list.GetEnumerator()) {
    if(enumerator.MoveNext())
        Console.WriteLine(enumerator.Current);

    list.RemoveRange(1, 0);


    if(enumerator.MoveNext()) // ** Does not throw
        Console.WriteLine(enumerator.Current);
    }
}
  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-05T13:41:13+00:00Added an answer on June 5, 2026 at 1:41 pm

    I would guess that this is intentional. C# follows a “pit of success” design – they want it to be hard to make a mistake.

    Ultimately, the existing design makes it easier to analyze the use of that method. What do I mean by that? Well:

    The example you cite is trivial, at a glance you can tell it isn’t really modifying the list. But in almost all real-world code that isn’t the case. The sequence being inserted would almost certainly be dynamically created, and could be an empty sequence almost randomly. Should an empty sequence really behave differently? Your code would work if the sequence inserted was empty, but the moment you put something real in there, ka-boom.

    Imagine if you first write this code and all your sequences are empty; looks like it works. Then, some arbitrary time later, you have a non-empty insert. Now you get exceptions. The distance between you inserting the problem and detecting it is potentially very large.

    With it throwing on any successful call, that failure mode becomes easier to detect.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I have this Code for Reading Excell Records: public IEnumerable<FillinEntity> Map(IEnumerable<ExcelRow> excelRows) { List<FillinEntity>
I am reading the following code, public static <t> T getFirst(List<T> list) I understand
I'm reading the book 'Beginning F#', There's a short list for example code, to
I often see this idiom when reading php code: public function __construct($config) { if
I was reading through some JMockit examples and found this code: final List<OrderItem> actualItems
Reading other people's code, I've seen a lot of: List<E> ints = new ArrayList<E>();
I'm reading code from GraphServer . And there is this function that has weird
I was reading Code Conventions for Java from http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-137265.html#587 . In that, they have
I am reading Code Complete. In that book, Steve McConnell warns that Developer tests
I have a simple text reading code for Visual Basic: Dim fileReader As String

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.