Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange, IEnumerable.ToList() creates entirely new objects

I know IEnumerable.ToList() is supposed to create a new List, but with the items pointing to the same original items in the IEnumerable, as discussed at ToList()-- Does it Create a New List?

However, I'm getting some strange behavior with my code using VS 2012; WPF; and .NET 4.0. It started when IEnumerable.SequenceEquals() seemed not to work as I expected. I dug around with my QuickWatch dialog, and, unbelievably, the following statement evaluates to false:

this.Items.First () == this.Items.ToList ()[ 0 ]

I even tried:

this.Items.ToList ().IndexOf(this.Items.First ())

which evaluated to -1.

Items is declared as a property on a WPF custom control, like so:

public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register (
        "Items", 
        typeof ( IEnumerable<UserLayoutType> ), 
        typeof ( UserLayoutSelectorControl ),
        new FrameworkPropertyMetadata ( null, FrameworkPropertyMetadataOptions.AffectsRender, UserLayoutSelectorControl.PropertyChanged ) );


public IEnumerable<UserLayoutType> Items
{
    get
    {
        return ( IEnumerable<UserLayoutType> ) this.GetValue ( UserLayoutSelectorControl.ItemsProperty );
    }
    set
    {    
        this.SetValue ( UserLayoutSelectorControl.ItemsProperty, value );                
    }
}

UserLayoutType is simply a class generated by the XSD tool, with the following declaration:

// 
// This source code was auto-generated by xsd, Version=4.0.30319.17929.
// 
namespace MyAssays.UserLayoutCore.UserLayoutUtility {
    using System.Xml.Serialization;


    /// <remarks/>
    [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.17929")]
    [System.SerializableAttribute()]
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.ComponentModel.DesignerCategoryAttribute("code")]
    [System.Xml.Serialization.XmlRootAttribute("UserLayout", Namespace="", IsNullable=false)]
    public partial class UserLayoutType {

This are the methods on a factory class that create the UserLayoutType Items in the first place:

public static IEnumerable<UserLayoutType> CreateFromFolder ( string folderPath )
    {
        if (String.IsNullOrEmpty(folderPath))
            throw new ArgumentNullException("folderPath", "Folder path must not be null");

        var userLayoutXmlFilePaths = Directory.GetFiles ( folderPath ).Where ( filePath => filePath.EndsWith ( ".UserLayout.xml", StringComparison.InvariantCultureIgnoreCase ) );
        return userLayoutXmlFilePaths.Select(filePath => UserLayoutFactory.CreateFromFile(filePath));
    }

    public static UserLayoutType CreateFromFile ( string filePath )
    {
        using ( var stream = new StreamReader ( filePath ) )
        {
            return ( UserLayoutType ) new XmlSerializer ( typeof ( UserLayoutType ) ).Deserialize ( stream );
        }
    }

Anybody have any idea what is happening? See image below: enter image description here

like image 976
prmph Avatar asked Aug 26 '13 03:08

prmph


1 Answers

The main, probable, cause for why you're seeing new objects from this is that the IEnumerable<T> is wrapping a generator, and not a materialized collection.

Here's a simple LINQPad program to demonstrate:

void Main()
{
    IEnumerable<string> collection =
        from index in Enumerable.Range(1, 10)
        select "Index=" + index;

    var list1 = collection.ToList();
    var list2 = collection.ToList();

    ReferenceEquals(list1[0], list2[0]).Dump();
}

This will print False.

It will do this because the act of enumerating over the collection (.ToList() in this case) will execute the deferred LINQ query, and since we're enumerating the collection twice, we execute it twice, producing different instances with the same values.

like image 156
Lasse V. Karlsen Avatar answered Nov 12 '22 15:11

Lasse V. Karlsen