oli
oli

Reputation: 1

Confusion over how to implement IEnumerable

I am new to IEnumerable in C# and am confused when reading about how to implement it from multiple sources. Here is what I have gathered so far: (1) source 1: If a class implements IEnumerable, it must implement IEnumerator, which in turn must implement MoveNext, Reset methods too. This means I need have all of these in the class:

class Product : IEnumerable
{
...
    public IEnumerator GetEnumerator()
        { return new ProductEnumerator();}
    ...
}
class ProductEnumerator : IEnumerator
{
...
    public bool MoveNext()
    {...}
   
    public void Reset()
    {...}

(2) Source 2: I can just simply use the below code and it is valid.

class Product
{
   public IEnumerable<Product> GetProducts() {return new List<Product>{...}}
}

(3) Source 3: When implementing the GetEnumerator method:

IEnumerator IEnumerable.GetEnumerator() 
{
    return (Enumerator) GetEnumerator();
}

My questions here:

  1. Why in Source 2 are we not using the interface syntax in declaring a class.e.g. public class Product:IEnumerator?
  2. Why is it in Source 2 we do not need to implement IEnumerator?
  3. What does the syntax in Source 3 actually mean? Are we declaring a method here? If yes, why is it not following the syntax of public IEnumerator GetEnumerator(){..}?

I am really quite confused and thanks for shining some light.

Upvotes: 0

Views: 434

Answers (1)

Dmitrii Bychenko
Dmitrii Bychenko

Reputation: 186688

When we want to enumerate anything, we usually start from IEnumerable<T> where T is the type of items we want to enumerate. Assuming that you have Product which can contain (sub-) products in List<Product> items

class Product {
  ...
  private List<Product> items = new List<Product>(); 
  ...
}

you can put it as (please, note that there's GetEnumerator and no more methods)

// Note, that if class implements IEnumerable<T>
// it implements IEnumerable as well.
class Product : IEnumerable<Product> {
  ...
  private List<Product> items = new List<Product>(); 
  ...

  public IEnumerator<Product> GetEnumerator() {
    throw new NotImplementedException();
  }

  // Usually, it's nothing more than boiler plate code:
  // Having typed GetEnumerator (above) we return typeless
  IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

Now we should implement IEnumerator<Product> GetEnumerator() method. We have two main options here: use the collection:

  ...
  // We want to enumerate items: let .Net do it for us
  public IEnumerator<Product> GetEnumerator() => items.GetEnumerator();
  ...

or some Linq query, e.g.

  ...
  // We want to enumerate items: let .Net do it for us
  public IEnumerator<Product> GetEnumerator() => items
    .Where(item => item.Name is not null) // Some condition here
    .GetEnumerator();
  ...

Another possibility is to implement custom enumeration and then call it via GetEnumerator():

  private Product MyEnumeration() {
    foreach (var item in items) {
      yield return item;
    }
  }

  public IEnumerator<Product> GetEnumerator() => MyEnumeration().GetEnumerator();

Upvotes: 0

Related Questions