.NET Forum / Languages / C# / October 2006
IEnumerable<> won't work
|
|
Thread rating:  |
Gustaf - 27 Oct 2006 11:52 GMT 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
Marc Gravell - 27 Oct 2006 11:59 GMT 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
Gustaf - 27 Oct 2006 12:44 GMT > IEnumerable<T> is itself derived from IEnumerable; the usual solution here > is to create an explicit implementation, and forward the request: [quoted text clipped - 6 lines] > 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
AbhishekTripathi - 27 Oct 2006 13:02 GMT 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.
Marc Gravell - 27 Oct 2006 14:06 GMT 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
AbhishekTripathi - 27 Oct 2006 18:48 GMT > 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.
Christof Nordiek - 30 Oct 2006 08:53 GMT 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.
>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. Jianwei Sun - 27 Oct 2006 13:06 GMT 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 .
>> IEnumerable<T> is itself derived from IEnumerable; the usual solution >> here is to create an explicit implementation, and forward the request: [quoted text clipped - 24 lines] > > Gustaf AbhishekTripathi - 27 Oct 2006 18:25 GMT 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(); } } }
Samuel R. Neff - 27 Oct 2006 19:17 GMT 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
------------------------------------------------------------ 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.
>see this demo code I created just to verify the topic being discussed here. > [quoted text clipped - 86 lines] > } >} AbhishekTripathi - 27 Oct 2006 19:38 GMT 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.
> foreach doesn't really use the interface--the compiler replaces it > automatically with a call to GetEnumerator so as long as the object [quoted text clipped - 13 lines] > > Sam Samuel R. Neff - 27 Oct 2006 20:06 GMT 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[i] = ((IClonable)array1[i]).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.
>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 [quoted text clipped - 25 lines] >> >> Sam AbhishekTripathi - 28 Oct 2006 05:12 GMT 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.
> The IClonable interface is needed when you want to provide clonable > functionality for a bunch of classes and don't care what actually [quoted text clipped - 81 lines] > development in MD/DC. Work with a variety of technologies > in a relaxed team environment. See ads on Dice.com. Samuel R. Neff - 31 Oct 2006 20:04 GMT Yes, that's a good idea. The recommendations were all good--just examples and implications were slightly off.
Best regards,
Sam
>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. ------------------------------------------------------------ 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.
Marc Gravell - 27 Oct 2006 20:34 GMT Ah; I misread the question as if the class were "implementing" the interface but not providing an actual implementation... but yes ;-p
Marc
Gustaf - 27 Oct 2006 18:26 GMT > I think you need both , something like the following. Yes! That works. Many thanks!
Gustaf
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|