c# Implementing two enumerators for the same class

523 views Asked by At

Ok, so here's the basic code:

class foo
{
    String name;
    int property;
}

class bar
{
    private List<foo> a;
    private List<foo> b;
}

I'd like to make it so that calling code can iterate over either list but I want to keep them protected from editing. I've looked into implementing the IEnumarable interface but the problem is that it expects a single "GetEnumerable" definition, but I want two different enumerators. For instance, i want to be able to say

foreach(foo in bar.getA())
{ //do stuff }

and then

foreach(foo in bar.getB())
{ //do stuff }

Do I have to subclass each element and implement the IEnumerable interface over each, and then include THOSE as properties? Am I misunderstanding the IEnumerable interface? I know that the List class has it's own Enumerator, so I could do something like

class bar
{
    private List<foo> a;
    private List<foo> b;

    public IEnumerator<foo> getAEnumerator()
    {  return a.GetEnumerator();

    public IEnumerator<foo> getBEnumerator()
    {  return b.GetEnumerator();

}

but then my for loops look like this:

bar x = new bar();
IEnumerator<foo> y = x.getAEnumerator();
while (y.moveNext())
{
    foo z = y.Current;
} 

so I lose the readability of "foreach".

Is there a way to accomplish using "foreach" over these lists without exposing these lists publicly? I'm still trying to get my head around the IENumerable interface, so maybe I'm missing something obvious.

2

There are 2 answers

2
Lucas Trzesniewski On BEST ANSWER

Don't expose a List<T>, expose something else, like an IReadOnlyList<T> instead:

class bar
{
    private readonly List<foo> a = new List<foo>();
    private readonly List<foo> b = new List<foo>();

    public IReadOnlyList<foo> A { get; private set; }
    public IReadOnlyList<foo> B { get; private set; }

    public bar()
    {
        A = a.AsReadOnly();
        B = b.AsReadOnly();
    }
}

Any changes to a and b will reflect in A and B.

Also note that while you can cast a List<T> to an IReadOnlyList<T>, the calling code can cast it back to List<T>. The above method returns a ReadOnlyCollection<T> which provides a safeguard against casting back to a mutable collection type.

The readonly keyword only ensures you don't substitute references to a and b with something else later on.

0
Anamta Khan On
class bar
{
    private readonly List<foo> a = new List<foo>();
    private readonly List<foo> b = new List<foo>();

    public IReadOnlyList<foo> A { get {return a.AsReadOnly();}}
    public IReadOnlyList<foo> B { get {return b.AsReadOnly();}}

}

this way you'll not even have to initialize it, and no need to any kind of set