tags:

views:

1795

answers:

4

I am trying to get the following code to compile but am getting errors in VS2008. Anyone can tell me where I am going wrong?

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

namespace dummy
{
    public class NaturalNumbersSequence : IEnumerable<int>
    {
        public IEnumerator<int> GetEnumerator()
        {
            for (int i = 1; i <= 1000; i++)
                yield return i;
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            for (int i = 1; i <= 1000; i++)
                yield return i;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            foreach (int i in new NaturalNumbersSequence())
                Console.WriteLine(i);
        }
    }
}
+11  A: 

Well, the first compiler error I get is that it complains that:

Using the generic type 'System.Collections.Generic.IEnumerator' requires '1' type arguments

This is on line 16, this one:

IEnumerator IEnumerable.GetEnumerator()

Fixing that by adding a using directive for the System.Collections namespace (tip: place the cursor just after IEnumerator, on the r at the end of the word, and hit Ctrl+. (ctrl + the dot-key), it should suggest you add a "using System.Collections;" directive, do that).

Then it compiles, and runs. Does that match what you expect?

Also, note that you should always post the actual error messages you're getting, this way we're not barking up the wrong tree if there's something else wrong with your code that we're not seeing at first glance.

Additionally, you can simplify this very common implementation of IEnumerable<T> by calling one of the methods from the other, hence I would simplify the implementation of the second methods like this:

IEnumerator IEnumerable.GetEnumerator()
{
    return GetEnumerator(); // this will return the one
                            // available through the object reference
                            // ie. "IEnumerator<int> GetEnumerator"
}

this way you only implement the actual enumerator code once.

And finally see Earwicker's answer as well, it shows a better (in my opinion at least) way to write this whole code.

Lasse V. Karlsen
+1. The last part about reusing one implementation of the enumerator on the other is the icing on the cake.
Martinho Fernandes
why are there 2 implementations of the enumerator in the first place?
fcuk112
+8  A: 

Not sure why you're getting errors, but wouldn't this be simpler?

public static class NumbersSequence
{
    public static IEnumerable<int> Naturals
    {
        get 
        {
            for (int i = 1; i <= 1000; i++)
                yield return i;
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        foreach (int i in NumbersSequence.Naturals)
            Console.WriteLine(i);
    }
}
Daniel Earwicker
And why stop at a thousand?!
Daniel Earwicker
Everybody knows that numbers above 1000 are unnaturally high.
Lasse V. Karlsen
For me, the distinction between "regular" and "large" numbers is at twenty thousand. I guess that is an "implementation" thing.
Martinho Fernandes
Twenty thousand is insanely high. Wait.... we're talking developer salaries here, right guys?
Lasse V. Karlsen
It's also interesting to see the age old dispute emerging here over whether the natural numbers start at zero or one!
Daniel Earwicker
If it's number theory, 1 is the only number. Everything else is the result of the successor operator.
maxwellb
This is beautiful.
kirk.burleson
+4  A: 

Slightly off-topic, but perhaps interesting to see is this approach:

public static class NumbersSequence
{
    public static IEnumerable<int> Naturals
    {
        get 
        {
            int i = 0;
            while(true)
                yield return i++;
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        foreach (int i in NumbersSequence.Naturals.Take(1000))
            Console.WriteLine(i);
    }
}

C# 3.0 afaik. Pay attention to the seemingly endless loop in the getter, and the call to Take(1000). With this method you now have an 'endless' sequence of natural numbers (endless up until the maxvalue of an int). It won't get stuck, as long as you tell it to take a specific amount.

DISCLAIMER: Most of the code borrowed from Earwicker's answer.

Erik van Brakel
So what's `i` in `Naturals.get`?
Pavel Minaev
Always nice to see "infinite" loops in action.
Martinho Fernandes
As Pavel noticed, you're missing initialization and increment of i.
Martinho Fernandes
I love me some Linq, but man I would definitely forget to .Take() in this case. I'd prefer to have Naturals be a method accepting an int parameter.
Stuart Branham
Shouldn't that be "yield return i++"?
rijipooh
This isn't right - you should declare `i` inside the `get`. The way you have it now, a second evaluation of `NumbersSequence.Naturals.Take(1000)` will return the numbers 1000 through 1999. Which should be the answer to `NumbersSequence.Naturals.Skip(1000).Take(1000)`
Daniel Earwicker
Instead of `while(true)`, I would do `while(i < int.MaxValue)`
Dennis Palmer
They should take away rep points for each edit.
kirk.burleson
+2  A: 

Lasse has the right answer, and I know this is learning code, but in the interest of furthering your learning I want to mention two things:

  1. Enumerable.Range()
  2. Take some time to think about this implementation:

.

public class NaturalNumbersSequence : IEnumerable<int>
{
    public IEnumerator<int> GetEnumerator()
    {
        for (int i=0 i <= int.MaxValue;i++)
            yield return i;
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        for (int i=0 i <= int.MaxValue;i++)
            yield return i;
    }
}

class Program
{
    static void Main(string[] args)
    {
        foreach (int i in new NaturalNumbersSequence().TakeWhile(i => i<=1000) )
            Console.WriteLine(i);
    }
}
Joel Coehoorn