Reputation: 2233
I noticed something interesting today when I was making changes for a pull request. Below is my code:
public List<MatColor> Colors { get; set; }
public List<Customer> Customers { get; set; }
public ProductViewModel()
{
this.Colors = new List<MatColor>();
this.Customers = new List<Customer>();
var products = this.LoadProductViewList();
this.LoadColorComboBox(products);
this.LoadCustomerComboBox(products);
}
public void LoadColorComboBox(IEnumerable<Product> products)
{
this.Colors.Clear();
this.Colors = products.Select(p => new MatColor()
{ Code = p.ColorCode, Description = p.ColorDescription })
.DistinctBy(p => m.Code).DistinctBy(p => p.Description).ToList();
}
private void LoadCustomerComboBox(IEnumerable<Product> products)
{
this.Customers.Clear();
this.Customers = products.Select(p => new Customer()
{ Name = p.CustomerName, Number = p.CustomerNumber })
.DistinctBy(p => p.Number).DistinctBy(p => p.Name).ToList();
}
This code does everything I want it to. It successfully populates both the Colors
and Customers
lists. I understand why it would always successfully populate the Colors
list. That's because LoadColorComboBox(...)
gets called first.
But an IEnumerable<T>
can only get enumerated, ONCE, right? So once it gets enumerated in LoadColorComboBox(...)
, how is it successfully getting reset and thus enumerated again in LoadCustomerComboBox(...)
? I've already checked the underlying type being returned by LoadProductViewList()
-- it calls a REST service which returns a Task<IEnumerable<Product>>
. Is my IEnumerable<Product>
somehow getting passed as a value? It's not a primitive so I was under the impression it's a reference type, thus, would get passed in by reference as default, which would cause the second method to blow up. Can someone please tell me what I'm not seeing here?
Upvotes: 4
Views: 3594
Reputation: 203837
And IEnumerable
is an object with a GetEnumerator
method, that is able to get you an enumerator. Generally one would expect it to be able to give you any number of enumerators. Some specific implementations might not support it, but the contract of the interface is that it should be able to give you as many as you want.
As for the IEnumerator
instances that it spits out, they do technically have a Reset
method, which is supposed to set it back to the "start" of the sequence. In practice however, most implementations tend to not support the "reset" operator and just throw an exception when you call the method.
In your case, you're not actually reseting an IEnumerator
and trying to use it to get the values of a sequence twice (which wouldn't work, as none of the iterators from the LINQ methods that you're using to create your sequence support being reset). What you're doing is simply getting multiple different enumerators, which those LINQ methods all support.
Upvotes: 11
Reputation: 52280
But an IEnumerable can only get enumerated, ONCE, right?
No. An IEnumerable<T>
can be enumerated any number of times. But for each enumeration, each item can be yielded only once.
In your case, products
will be enumerated once for each LINQ query that needs the list of products... so once in LoadColorComboBox
and once in LoadCustomerComboBox
.
Upvotes: 2