2
votes

I want to define a collection with an enumerator that can be set at run time.

I defined an enumerator:

public class JuniorEmployeeEnumerator : IEnumerable<Employee> {
    List<Employee> list = new List<Employee>();

    public JuniorEmployeeEnumerator(List<Employee> emplist) {
        list = emplist;
    }
    public IEnumerator<Employee> GetEnumerator() {
        for (int i = 0; i < list.Count; i++) {
            if (list[i].Age < 50) {
                yield return list[i];
            }
        }
    }
    IEnumerator IEnumerable.GetEnumerator() {
        return (GetEnumerator());
    }
}

And the collection accepts the enumerator:

public class EmployeeCollection : IEnumerable<Employee> {
    public List<Employee> Employees { get; set; }
    public IEnumerable<Employee> EnumeratorType { get; set; }
    public EmployeeCollection() {
        Employees = new List<Employee>();
    }
    public IEnumerator<Employee> GetEnumerator() {
        return EnumeratorType.GetEnumerator();
    }

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

So, the usage should be as:

static void Main(string[] args) {
    EmployeeCollection empcoll = new EmployeeCollection();

    empcoll.Employees.Add(new Employee("Fatima", 57));
    empcoll.Employees.Add(new Employee("Evangeline", 52));
    empcoll.Employees.Add(new Employee("Damien", 49));
    empcoll.Employees.Add(new Employee("Cameroon", 55));
    empcoll.Employees.Add(new Employee("Babu", 24));

    Console.Write("Senior Employees \n");

    empcoll.EnumeratorType = new JuniorEmployeeEnumerator(empcoll.Employees);

    foreach (Employee anEmp in empcoll.GetEnumerator()) {
        Console.Write("   " + anEmp.Name + "   " + anEmp.Age + "\n");
    }
    Console.ReadKey();
}

But i get this compilation error:

foreach statement cannot operate on variables of type 'System.Collections.Generic.IEnumerator' because 'System.Collections.Generic.IEnumerator' does not contain a public definition for 'GetEnumerator'

How can I fix it?

3

3 Answers

3
votes

Use foreach to loop over IEnumerable, not IEnumerator.

Thus, instead of

foreach (Employee anEmp in empcoll.GetEnumerator()) {
    Console.Write("   " + anEmp.Name + "   " + anEmp.Age + "\n");
}

You could do:

foreach (Employee anEmp in empcoll) {
    Console.Write("   " + anEmp.Name + "   " + anEmp.Age + "\n");
}

Basically, the foreach keyword is just syntactic sugar for:

using (var enumerator = enumerable.GetEnumerator()) {
    T loopVar;
    while (enumerator.MoveNext()) {
        loopVar = enumerator.Current;
        ... loop body ...
    }
}

That said, the LINQ operators (e. g. Where()), might be a better solution to the problem you're trying to solve:

List<Employee> emps = ...;
foreach (var junior in emps.Where(e => e.Age < 50)) {
   ...
}
0
votes

You've got it wrong. Enumerators are basically how C# deals with getting all the parts of the enumerator. You don't need to worry about those, as all collection classes have implemented those for you. What you seem to be looking for is the Enumerable.Where extension method, located in System.Linq. Here's the code with it:

public class EmployeeCollection : IEnumerable<Employee>
{
    public List<Employee> Employees { get; set; }
    public EmployeeCollection()
    {
        Employees = new List<Employee>();
    }
    public IEnumerator<Employee> GetEnumerator()
    {
        return Employees.GetEnumerator();
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return Employees.GetEnumerator();
    }
}
public class X
{
    static void Main(string[] args)
    {
        EmployeeCollection empcoll = new EmployeeCollection();
        empcoll.Employees.Add(new Employee("Fatima", 57));
        empcoll.Employees.Add(new Employee("Evangeline", 52));
        empcoll.Employees.Add(new Employee("Damien", 49));
        empcoll.Employees.Add(new Employee("Cameroon", 55));
        empcoll.Employees.Add(new Employee("Babu", 24));
        Console.Write("Senior Employees \n");
        foreach (Employee anEmp in empcoll.Where(n => n.Age > 50))
        {
            Console.WriteLine("   " + anEmp.Name + "   " + anEmp.Age);
        }
        Console.ReadKey(true);
    }
}
0
votes

You are making things much too complicated for yourself. Modern C# ensures that you basically never have to write custom enumerators, especially for built-in collection classes.

This is mostly made possible through LINQ, which provides a number of ways to filter and project elements of a collection into a new enumerable.

For example, given your employees list, if you want to get all the junior employees, you can do so like this:

foreach (Employee junior in empColl.Employees.Where(emp => emp.Age < 50))
{
    // Do something with the junior employee object
}