IEnumerable<> won't work

G

Gustaf

Using VS 2005. I got an 'IpForm' class and an 'IpFormCollection' class,
containing IpForm objects. To iterate through IpFrom objects with
foreach, the class is implemented as such:

public class IpFormCollection : IEnumerable<IpForm>
{
ArrayList forms = new ArrayList();

public IEnumerator<IpForm> GetEnumerator()
{
foreach (IpForm f in this.forms)
{
yield return f;
}
}

...
}

From what I learned here

http://www.ondotnet.com/pub/a/dotnet/2004/06/07/liberty.html

this ought to work, but VS says that the class "does not implement
interface member 'System.Collections.IEnumerable.GetEnumerator()'." and
that "GetEnumerator()' is either static, not public, or has the wrong
return type."

Gustaf
 
M

Marc Gravell

IEnumerable<T> is itself derived from IEnumerable; the usual solution here
is to create an explicit implementation, and forward the request:

IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator(); // returns the public, typed one
}

Also, if this is 2.0, I would make forms a List<IpForm>; this is then
strongly typed, and has the side-advantage that you can drop an enumerator:
simply return this.forms.GetEnumerator() instead of the foreach/yield return

See if that helps...

Marc
 
G

Gustaf

Marc said:
IEnumerable<T> is itself derived from IEnumerable; the usual solution here
is to create an explicit implementation, and forward the request:

IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator(); // returns the public, typed one
}

Also, if this is 2.0, I would make forms a List<IpForm>; this is then
strongly typed, and has the side-advantage that you can drop an enumerator:
simply return this.forms.GetEnumerator() instead of the foreach/yield return

Thank you. Do you mean replacing the GetEnumerator() code, like this?

public class IpFormCollection : IEnumerable<IpForm>
{
List<IpForm> forms = new List<IpForm>();

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

I'm getting another error now. It says the collection class "does not
implement interface member
'System.Collections.Generic.IEnumerable<MyNamespace.IpForm>.GetEnumerator()'".

Gustaf
 
G

Guest

I have a related question. A class gets a foreach iteration capability when
public IEnumerator GetEnumerator() method is used without actually
implementing the interface in the class. Why is it so and why isnt the
interface implementation necessary? Same question applies to IClonable as
well. Kindly explain in a little detail.
 
J

Jianwei Sun

I think you need both , something like the following.

public class IpFormCollection : IEnumerable<IpForm>
{
ArrayList forms = new ArrayList();

public IEnumerator<IpForm> GetEnumerator()
{
foreach (IpForm f in this.forms)
{
yield return f;
}
}

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

Also , you can inherit from List<IpForm> instead of implementing all
your interface manually .

Gustaf wrote:
 
M

Marc Gravell

No; if it isn't implemented it won't work... did you have a specific example
in mind? To either interface... (perhaps this is provided by a base class?)

Marc
 
G

Guest

see this demo code I created just to verify the topic being discussed here.

using System;
using System.Collections;

namespace InterfaceDemo
{
public class Car:IComparable
{
public string name;
public int speed;

Car() { }
public Car(String N, int S)
{
name = N;
speed = S;
}
//see this provides Clonability without explicit IClonable implementation
public object Clone()
{
return this.MemberwiseClone();
}

public override string ToString()
{
return string.Format("Name: {0}, Speed: {1}", this.name,
this.speed);
}

int IComparable.CompareTo(Object o)
{
Car c = (Car)o;
return ((this.speed > c.speed) ? 1 : -1);
}
}


public class Garage
{
public Car[] carArray;
public Garage()
{
carArray = new Car[4];
carArray[0] = new Car("Rusty", 30);
carArray[1] = new Car("Clunker", 55);
carArray[2] = new Car("Zippy", 30);
carArray[3] = new Car("Fred", 30);
}
//Provides functionality of foreach access to the class
//Note: no IEnumerable implemented
public IEnumerator GetEnumerator()
{
return carArray.GetEnumerator();
}
}
}



using System;
using System.Collections.Generic;
using System.Text;

namespace InterfaceDemo
{
class Program
{
static void Main(string[] args)
{
Garage carLot = new Garage();
foreach (Car c in carLot)
{
Console.WriteLine("{0} is going {1} MPH", c.name, c.speed);
}
Car c1 = new Car("OriginalCar", 100);
Console.WriteLine(c1.ToString());
Car c2 = (Car)c1.Clone();
Console.WriteLine("Cloned Object: {0}", c2.ToString());
Garage g = new Garage();
try
{
Array.Sort(g.carArray); //calls IComparable.CompareTo from
Car implicitly
}
catch (Exception e)
{
Console.WriteLine("->Exception: {0}", e.Message);
}
Console.ReadLine();
}
}
}
 
G

Guest

Marc Gravell said:
No; if it isn't implemented it won't work... did you have a specific example
in mind? To either interface... (perhaps this is provided by a base class?)

Marc

I am not much aware of using newsgroups. I use webforums which are a little
different. Looking at the thread hierarchy I think I posted my code sample in
another thread probably. Incase that is the issue, kindly look at the code
sample in the appropriate discussion thread.
 
S

Samuel R. Neff

foreach doesn't really use the interface--the compiler replaces it
automatically with a call to GetEnumerator so as long as the object
has GetEnumerator then it'll compile (regardless of interface
implementation).

Array.Sort is runtime and does require an interface. If you took off
IComparable then I'm sure your example would produce a cast exception
at runtime.

The clonable example isnt' using the interface at all--the sample code
simply calls a public method on an object that is already typed as
car. It's a function called Clone but there's no reference to the
IClonable interface so no surprises there.

HTH,

Sam
 
G

Guest

Correct me if i am wrong. ICloneable provides cloneable to the class with
the Clone(Object) function. Same is implemented here but I wanted to show
that this feature actually can be implemented without implementing the
interface explicitly. Even if you implement the Interface the program still
compiles well. So what is the difference in using ICloneable or not ? Also,
as per the book (Apress,AndrewTroelsen) the class acquires the foreach
iterative capability by the interface IEnumerable. But I found again that
this capability doesnt really check the type of class to see interface
implementation. Please answer these problems and also provided a more precise
code which shows the necessary explicit implementation of the interfaces in
discussion.
 
S

Samuel R. Neff

The IClonable interface is needed when you want to provide clonable
functionality for a bunch of classes and don't care what actually
implements the class.

Say you have:

class A : IClonable {
public object Clone() { ... }
}

class B : IClonable {
public object Clone() { ... }
}


and then you had an object array

object[] array1 = new object[] { new A(), new B() };

you can clone the array like this:

object[] array2 = new object[array1.length];
for(int i=0; i<array1.length; i++) {
array2 = ((IClonable)array1).Clone();
}

There you've provided clonability for an object even though the
calling code doesn't care what class it is--only that it implements
IClonable.

Andrew Troelsen has 14 books on Amazon so I don't know exactly which
book you're referring to. However, I have one of his books, C# and
the .NET Platform (second edition) in front of me and look at Chapter
6: Interfaces and Collections he has a section on "Building a custom
Enumerator (IEnumerable and IEnumerator). What he writes in this
section is wrong and a horrible example of how to do what he's
explaining.

First he says that if you try to use foreach on a class that doesn't
have IEnumerable or GetEnumerator, he says that the compiler will
complain 'cause the class "does not implement the GetEnumerator()
method." This is correct. However, he goes on to say that
GetEnumerator() is defined in IEnumerable and we can add
GetEnumerator() to our class by making our class implement
IEnumerable.

This is literally correct but the implication is wrong. IEnumerable
does provide the declaration of GetEnumerator() and if you're
implementing GetEnumerator() then you should always implement
IEnumerable in order to conform with standards and use expectations,
however it isn't technically correct that you need to implement
IEnumerable to use foreach. The compiler looks for GetEnumerator()
method only, it doesn't look for the interface.

Some people may write code that can work on an IEnumerable class or
make custom calls to GetEnumerator() and for that reason you should
always implement IEnumerable when providing a GetEnumerator()
method--but it's not absolutely required (as you found out).

Then he goes on to implement GetEnumerator() by adding custom
enumeration functionality to the Car class itself. NO NO NO, don't
ever do this. If two threads try to enumerate the same collection
they'll overwrite each other's position and create huge problems. If
you try to iterate the same collection twice in a nested loop, it'll
have the same problem. If you change the collection while enumerating
you could end up hitting the same item twice or aborting prematurely
(the enumerator is technically supposed to throw an error at this
point). If you want a good example of how to impelment an enumerator
then get Reflector and look at how it's done in the framework classes.
Don't follow Andrew Troelsen's recommendations.

I also see the same book has a discussion of IClonable with a sample
very similar to the one you showed. I'm sorry to say that the sample
shows what Clone() is supposed to do but it doesn't show why you
implement IClonable. Hopefully the above sample helped to clear
things up for you.

Best regards,

Sam
 
M

Marc Gravell

Ah; I misread the question as if the class were "implementing" the
interface but not providing an actual implementation... but yes ;-p

Marc
 
G

Guest

Hey buddy that was a good answer indeed. The book I referred to is the same
as you mentioned i.e. "C# 2005 and the .NET 2.0 Platform". I was going word
by word with the book. But your elaboration really made me rethink over my
presumptions about the author. Nevertheless, I will try not to stick to its
recommendations.



Samuel R. Neff said:
The IClonable interface is needed when you want to provide clonable
functionality for a bunch of classes and don't care what actually
implements the class.

Say you have:

class A : IClonable {
public object Clone() { ... }
}

class B : IClonable {
public object Clone() { ... }
}


and then you had an object array

object[] array1 = new object[] { new A(), new B() };

you can clone the array like this:

object[] array2 = new object[array1.length];
for(int i=0; i<array1.length; i++) {
array2 = ((IClonable)array1).Clone();
}

There you've provided clonability for an object even though the
calling code doesn't care what class it is--only that it implements
IClonable.

Andrew Troelsen has 14 books on Amazon so I don't know exactly which
book you're referring to. However, I have one of his books, C# and
the .NET Platform (second edition) in front of me and look at Chapter
6: Interfaces and Collections he has a section on "Building a custom
Enumerator (IEnumerable and IEnumerator). What he writes in this
section is wrong and a horrible example of how to do what he's
explaining.

First he says that if you try to use foreach on a class that doesn't
have IEnumerable or GetEnumerator, he says that the compiler will
complain 'cause the class "does not implement the GetEnumerator()
method." This is correct. However, he goes on to say that
GetEnumerator() is defined in IEnumerable and we can add
GetEnumerator() to our class by making our class implement
IEnumerable.

This is literally correct but the implication is wrong. IEnumerable
does provide the declaration of GetEnumerator() and if you're
implementing GetEnumerator() then you should always implement
IEnumerable in order to conform with standards and use expectations,
however it isn't technically correct that you need to implement
IEnumerable to use foreach. The compiler looks for GetEnumerator()
method only, it doesn't look for the interface.

Some people may write code that can work on an IEnumerable class or
make custom calls to GetEnumerator() and for that reason you should
always implement IEnumerable when providing a GetEnumerator()
method--but it's not absolutely required (as you found out).

Then he goes on to implement GetEnumerator() by adding custom
enumeration functionality to the Car class itself. NO NO NO, don't
ever do this. If two threads try to enumerate the same collection
they'll overwrite each other's position and create huge problems. If
you try to iterate the same collection twice in a nested loop, it'll
have the same problem. If you change the collection while enumerating
you could end up hitting the same item twice or aborting prematurely
(the enumerator is technically supposed to throw an error at this
point). If you want a good example of how to impelment an enumerator
then get Reflector and look at how it's done in the framework classes.
Don't follow Andrew Troelsen's recommendations.

I also see the same book has a discussion of IClonable with a sample
very similar to the one you showed. I'm sorry to say that the sample
shows what Clone() is supposed to do but it doesn't show why you
implement IClonable. Hopefully the above sample helped to clear
things up for you.

Best regards,

Sam


------------------------------------------------------------
We're hiring! B-Line Medical is seeking Mid/Sr. .NET
Developers for exciting positions in medical product
development in MD/DC. Work with a variety of technologies
in a relaxed team environment. See ads on Dice.com.
 
C

Christof Nordiek

It works, because the C#-compiler looks for the Enumerable pattern, not only
for Enumerable class or interface.
That's because foreach entered C# prior to generics. So it was possible to
use a strongtyped Enumerator with foreach, without the need of a generic
enumerator-interface.
 
S

Samuel R. Neff

Yes, that's a good idea. The recommendations were all good--just
examples and implications were slightly off.

Best regards,

Sam
 

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