Implement Interface


T

Tony Johansson

Hello!

I have a generic BinaryTree called Tree<T> below where I have implemented
the IEnumerable<T> interface.
The class header is shown below.
public class Tree<T> : IEnumerable<T> where T : IComparable<T>
{
....
....
}
I have also another class called TreeEnumerator<T> where the interface
IEnumerator<T> has been implemented.
The header is shown below.
class TreeEnumerator<T> : IEnumerator<T> where T : IComparable<T>
{
....
...
}

When VS generate the skeleton for the interface implementation IEnumerable
in class Tree<T>
it using this type of construction.
IEnumerator IEnumerable.GetEnumerator()
{
return new TreeEnumerator<T>(this);
}

I have also a Testclass to test the Tree<T> and TreeEnumerator<T>.

Now to my question.
I noticed in the Testclass that method GetEnumerator was not shown in the
Intelligens
for example if I did the following Tree<int> myTree = new Tree<int>(12);
and wrote myTree. no GetEnumerator was shown.
Why ?
If I write System.Collections.IEnumerator iter = myTree.GetEnumerator();
I get compile error saying no definition for GetEnumerator.
So what access is it on the GetEnumerator ?

I then changed the GetEnumerator in the Tree<T> class to this instead
public IEnumerator GetEnumerator()
{
return new TreeEnumerator<T>(this);
}

Now I can for example do System.Collections.IEnumerator iter =
myTree.GetEnumerator();
and intelligens shows GetEnumerator()

So as a summary I want to have an answer on two questions.
Question 1. What access is it on the GetEnumerator() when having this
construction
IEnumerator IEnumerable.GetEnumerator()
{
return new TreeEnumerator<T>(this);
}

Question 2. What is the recommended way to implement is it to use
IEnumerator IEnumerable.GetEnumerator()
{
return new TreeEnumerator<T>(this);
}
which mean that direct access to GetEnumerator is not allowed like
System.Collections.IEnumerator iter = myTree.GetEnumerator();
or writing
public IEnumerator GetEnumerator()
{
return new TreeEnumerator<T>(this);
}

I know that is some cases you have no choice you must use the explicit
implementation
but in those cases when you can choose which one do you recommend ?. I thing
that using this one is better
public IEnumerator GetEnumerator()
{
return new TreeEnumerator<T>(this);
}
because I can use for example this construction
System.Collections.IEnumerator iter = myTree.GetEnumerator();
and Intelligens work with GetEnumerator

PS A long posting hope you understand what I mean.

//Tony
 
Ad

Advertisements

M

Marc Gravell

it using this type of construction.
IEnumerator IEnumerable.GetEnumerator()
{
   return new TreeEnumerator<T>(this);>
}
...method GetEnumerator was not shown in the Intelligens...
...Why ?
Because you have used explicit implementation. If you had used
implicit implementation it would show:
public IEnumerator GetEnumerator()
If you are implementing IEnumerable<T>, it is common to use explicit
implementation for the untyped IEnumerable version, and implicit
implementation for the typed IEnumerable<T> version [of
GetEnumerator()], with the IEnumerable version simply return the
public GetEnumerator():

public IEnumerator<T> GetEnumerator() {...whatever...}
IEnumerator IEnumerable.GetEnumerator() {return GetEnumerator();}

However, "foreach" should be able to use either implicit or explicit
construction without any issues.

I think that addresses most of Q1 and Q2 (it was long, so maybe I
missed something); perhaps post anything I've missed?

For info, in C# 2 it is uncommon to write your own IEnumerator[<T>]
implementation; you can get the compiler to do it for you very
effectively (and less error-prone). For instance:

public IEnumerator<int> GetEnumerator() {
for(int i = 0; i < 100; i++) {
yield return i;
}
}

Actually creates a hidden type that does everything necessary as a
state machine to return 0-99 as a foreach result. Or to illustrate
from unioning 2 lists:

public IEnumerator<T> GetEnumerator() {
foreach(T t in firstList) {
yield return t;
}
foreeach(T t in secondList) {
yield return t;
}
}

You might find you can write your enumerator a lot easier using "yield
return" and "yield break".

Put it this way: I haven't written a single IEnumerator[<T>]
implementation sinc moving to .NET 2.0, but I've written lots of
"yield return"...

Marc
 
M

Murkland

Hello!

I have a generic BinaryTree called Tree<T> below where I have implemented
the IEnumerable<T> interface.
The class header is shown below.
public class Tree<T> : IEnumerable<T> where T : IComparable<T>
{
...
...}

I have also another class called TreeEnumerator<T> where the interface
IEnumerator<T> has been implemented.
The header is shown below.
class TreeEnumerator<T> : IEnumerator<T> where T : IComparable<T>
{
...
..

}

When VS generate the skeleton for the interface implementation IEnumerable
in class Tree<T>
it using this type of construction.
IEnumerator IEnumerable.GetEnumerator()
{
   return new TreeEnumerator<T>(this);

}

I have also a Testclass to test the Tree<T> and TreeEnumerator<T>.

Now to my question.
I noticed in the Testclass that method GetEnumerator was not shown in the
Intelligens
for example if I did the following Tree<int> myTree = new Tree<int>(12);
and wrote myTree. no GetEnumerator was shown.
Why ?
If I write System.Collections.IEnumerator iter = myTree.GetEnumerator();
I get compile error saying no definition for GetEnumerator.
So what access is it on the GetEnumerator ?

I then changed the GetEnumerator in the Tree<T> class to this instead
public IEnumerator GetEnumerator()
{
   return new TreeEnumerator<T>(this);

}

Now I can for example do System.Collections.IEnumerator iter =
myTree.GetEnumerator();
and intelligens shows GetEnumerator()

So as a summary I want to have an answer on two questions.
Question 1. What access is it on the GetEnumerator() when having this
construction
IEnumerator IEnumerable.GetEnumerator()
{
   return new TreeEnumerator<T>(this);

}

Question 2. What is the recommended way to implement is it to use
IEnumerator IEnumerable.GetEnumerator()
{
   return new TreeEnumerator<T>(this);}

which mean that direct access to GetEnumerator is not allowed like
System.Collections.IEnumerator iter = myTree.GetEnumerator();
or writing
public IEnumerator GetEnumerator()
{
   return new TreeEnumerator<T>(this);

}

I know that is some cases you have no choice you must use the explicit
implementation
but in those cases when you can choose which one do you recommend ?. I thing
that using this one is better
public IEnumerator GetEnumerator()
{
   return new TreeEnumerator<T>(this);}

because I can use for example this construction
System.Collections.IEnumerator iter = myTree.GetEnumerator();
and Intelligens work with GetEnumerator

PS A long posting hope you understand what I mean.

//Tony

Hello!

Since you implement the IEnumerable<> interface, which in turn
implements the non-generic IEnumerable interface, you want to use the
generic version's GetEnumerator method.

Try this:
// Generic implementation
public IEnumerator<T> GetEnumerator()
{
foreach (T node in theTree)
yield return T;
}

Since the generic version implements the non-generic, you want the non-
generic GetEnumerator method to call your generic one.
// Hide the non-generic implementation and return the generic
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

You will then be able to enumerate over the collection by either doing
a foreach or by getting the enumerator manually.
 
M

Marc Gravell

Additional; Jon Skeet's new book (C# In Depth) has a really good
explanation of "yield return" etc; the entire of chapter 6, in fact -
covering all the subtleties, nuances, etc. Not yet in print, but you
can get the "early access" version as PDF.

I see I missed a question:
which one do you recommend ?. I thing
that using this one is better
public IEnumerator GetEnumerator()
{
return new TreeEnumerator<T>(this);
}

You can't do that if you are using implicit implementation for the
typed version, as you can't (in C#) have overloads that differ only by
the return type - i.e.
IEnumerator GetEnumerator() {...}
and
IEnumerator<T> GetEnumerator() {...}
would clash.
And given that the second is the preferred option, it is this that is
normally exposed on the public API, using explicit implementation for
the untyped version.

Marc
 
T

Tony Johansson

Hello!

In your posting you talk about typed version and untyped version can you
please explain exactly what you mean by that ?

//Tony
 
M

Marc Gravell

In your posting you talk about typed version and untyped version can you
pease explain exactly what you mean by that ?

..NET 1.x had the IEnumerable/IEnumerator interfaces, but not
"generics", and so IEnumator is defined as having:
object Current { get; }
There are three problems here:
* the caller has to keep casting the Current value to whatever they
expect it to be
* the compiler can't verify much for you
* it needs to "box" value-types (int, float, etc) [simply to be
unboxed when you cast it back]

..NET 2.0 introduced generics, and provided the IEnumerable<T>/
IEnumerator<T> interfaces. The main change is that IEnumerator<T> has:
T Current { get; }

i.e. if you implement IEnumerable<Foo>, the compiler knows that each
value (from the enumerator) is a Foo:
* no need to cast
* the compiler can verify that you aren't doing anything clearly wrong
* there is no need to box/unbox value-types

The decision was taken that IEnumerable<T> : IEnumerable, which means
that if you implement IEnumerable<T>, then any code that only knows
about IEnumerable can still use your enumerator perfectly happily, but
using the older "object" approach. But because the GetEnumerable()
method now exists twice, you need to make it clear to the compiler
which is which, by using explicit implementation of
IEnumerable.GetEnumerator() [the IEnumerable<T>.GetEnumerable()
version being preferred on the public interface for all the reasons I
have already listed].

Marc
 
Ad

Advertisements

T

Tony Johansson

Hello!

Below I have two different versions(both use explicit) the first one is the
typed version and the second one is untyped version. I think.
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{ return new TreeEnumerator<T>(this); }

IEnumerator IEnumerable.GetEnumerator() explicit
{ throw new Exception("The method or operation is not implemented."); }

So you mean in this case the most common is to change the typed version
using implicit decalaration like the below.
public IEnumerator<T> GetEnumerator()
{ return new TreeEnumerator<T>(this); }

Have I understood you correctly ?

//Tony
 
Ad

Advertisements

M

Marc Gravell

So you mean in this case the most common is to change the typed version
using implicit decalaration like the below

That is exactly what I mean. Of course, if you are only using
"foreach" etc, it probably won't matter much either way - but that is
the common approach.

Marc
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top