Question

I'm having trouble understanding why multiple calls of Contains return different values for the same parameter on the same enumerable. While I understand that the collection can be modified, thus changing the result in a subsequent call, this can be ruled out here.

Consider the following (stripped-down) code in an MVC view. The purpose of this will be to display a list of checkboxes (as there's no HTML-helper for that), and determining through the model's properties which ones should be checked when opening the view.

@foreach (var d in Model.AllDomains) {
    bool isChecked = Model.Project.Domains.Contains(d.ID);
    <input @(isChecked ? "checked=\"checked\" " : "")type="checkbox" value="@d.ID" />
    // more stuff here
}

Changing this to use an actual List makes the whole thing work as expected:

var tmp = Model.Project.Domains.ToList();
@foreach (var d in Model.AllDomains) {
    bool isChecked = tmp.Contains(d.ID);
    <input @(isChecked ? "checked=\"checked\" " : "")type="checkbox" value="@d.ID" />
    // more stuff here
}

The following is the model that is bound to my view (again simplified to make it more readable):

public ProjectVM GetByID(int id) {
    return new ProjectVM {
        Project = new Project {
            ... // Other properties here
            Domains = from d in MyObjectModel.Projects[id].Domains
                      select d.ID
        },
        AllDomains = from d in MyObjectModel.Domains
                     orderby d.Name
                     select new {
                         ID = d.ID,
                         Name = d.Name
                     }
    };
}

Now, while from debugging I know that Model.Project.Domains will contain the correct number of entries, as well as the correct values, calling .Contains() on the method returns an arbitrary result - either true or false.

In fact, if I put the line with the Contains() call into the debugger's "Watch" tab multiple times, even with an hard coded argument (e.g. 4) the result will alternate from true to false with every call.

What is happening here, what am I overlooking?

Because of the way that Model.Project.Domains is instantiated, its actual type is a WhereSelectEnumerableIterator<T>, but this implements IEnumerable<T> so that shouldn't be an issue...

Was it helpful?

Solution

It seems that the root cause of the problem was a sloppy/unusual implementation of the Enumerator in the foundation classes of our object model which made GetEnumerator() return an iterator which was already used in the previous call

Since Contains() stops iterating over the collection after the first match is found, it would return false on such an Enumerator if the seeked value was in the part which had already been searched in the previous iteration. A negative result of Contains() caused the enumerator to reset internally, which explained the "toggling" result described in my original post.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top