problem using IEnumerable<T>

  • Thread starter Thread starter jcc
  • Start date Start date
J

jcc

Hi guys,

I'm a newbie to C#. My Visual Studio 2005 failed to compile the
following code with error as
'HelloWorld.A' does not implement interface member
'System.Collections.IEnumerable.GetEnumerator()'.
'HelloWorld.A.GetEnumerator()' is either static, not public, or has the
wrong return type.

class A : IEnumerable<string>
{
string[] items = new string[100];

public IEnumerator<string> GetEnumerator()
{
foreach (string s in items)
yield return s;
}
}

Could you tell me what's wrong? It does not compile even if I copy the
example code from Programming C#, 4th Edition, although PC#'s example
is different with mine.

Thanks!
 
Did you read the error message? It says you didn't implement an interface
member, which you didn't... you implemented
System.Collections.Generic.IEnumerable<string>.GetEnumerator(), but you
didn't implement System.Collections.IEnumerable.GetEnumerator().

You'll probably want an explicit interface implementation, so add this to
your class:

System.Collections.IEnumerable System.Collections.IEnumerable.GetEnumerator()
{
return null;
}
 
don't think ur answer is correct. I did implement GetEnumerator(). it's
public, not static, and I can't find the return type is wrong.

The following code is copied from programming c# 4th edition, VS 2005
did not compile it either.

#region Using directives

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

#endregion

namespace Enumerable
{
public class ListBoxTest : IEnumerable<String>
{
private string[] strings;
private int ctr = 0;
// Enumerable classes can return an enumerator
public IEnumerator<string> GetEnumerator( )
{
foreach ( string s in strings )
{
yield return s;
}
}

// initialize the list box with strings
public ListBoxTest( params string[] initialStrings )
{
// allocate space for the strings
strings = new String[8];

// copy the strings passed in to the constructor
foreach ( string s in initialStrings )
{
strings[ctr++] = s;
}
}

// add a single string to the end of the list box
public void Add( string theString )
{
strings[ctr] = theString;
ctr++;
}

// allow array-like access
public string this[int index]
{
get
{
if ( index < 0 || index >= strings.Length )
{
// handle bad index
}
return strings[index];
}
set
{
strings[index] = value;
}
}

// publish how many strings you hold
public int GetNumEntries( )
{
return ctr;
}
}

public class Tester
{
static void Main( )
{
// create a new list box and initialize
ListBoxTest lbt =
new ListBoxTest( "Hello", "World" );

// add a few strings
lbt.Add( "Who" );
lbt.Add( "Is" );
lbt.Add( "John" );
lbt.Add( "Galt" );

// test the access
string subst = "Universe";
lbt[1] = subst;

// access all the strings
foreach ( string s in lbt )
{
Console.WriteLine( "Value: {0}", s );
}
}
}
}
 
don't think ur answer is correct. I did implement GetEnumerator().

My previous answer is correct; did you try doing what I said? Listen closely:

You implemented System.Collections.Generic.IEnumerable<string>

You DID NOT implement System.Collections.IEnumerable

They are NOT the same thing. This is what the compiler is complaining about.
 
KH said:
My previous answer is correct; did you try doing what I said? Listen
closely:

You implemented System.Collections.Generic.IEnumerable<string>

You DID NOT implement System.Collections.IEnumerable

They are NOT the same thing. This is what the compiler is complaining
about.

But

System.Collections.IEnumerable
System.Collections.IEnumerable.GetEnumerator()
{
return null;
}

is also wrong. This will break (at runtime) any class which tries to
enumerate this collection through its System.Collections.IEnumerable
interface.


Instead you should just return IEnumerable<string>.GetEnumerator() since
IEnumerator<string> implements IEnumerator.


System.Collections.IEnumerable
System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

Here's the complete sample.

David


#region Using directives

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

#endregion

namespace Enumerable
{
public class ListBoxTest : IEnumerable<String>,IEnumerable
{
private string[] strings;
private int ctr = 0;
// Enumerable classes can return an enumerator
public IEnumerator<string> GetEnumerator()
{
foreach (string s in strings)
{
yield return s;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}


// initialize the list box with strings
public ListBoxTest(params string[] initialStrings)
{
// allocate space for the strings
strings = new String[8];

// copy the strings passed in to the constructor
foreach (string s in initialStrings)
{
strings[ctr++] = s;
}
}

// add a single string to the end of the list box
public void Add(string theString)
{
strings[ctr] = theString;
ctr++;
}

// allow array-like access
public string this[int index]
{
get
{
if (index < 0 || index >= strings.Length)
{
// handle bad index
}
return strings[index];
}
set
{
strings[index] = value;
}
}

// publish how many strings you hold
public int GetNumEntries()
{
return ctr;
}

}

public class Tester
{
static void Main()
{
// create a new list box and initialize
ListBoxTest lbt =
new ListBoxTest("Hello", "World");

// add a few strings
lbt.Add("Who");
lbt.Add("Is");
lbt.Add("John");
lbt.Add("Galt");

// test the access
string subst = "Universe";
lbt[1] = subst;

// access all the strings
foreach (object s in lbt)
{
Console.WriteLine("Value: {0}", s);
}
}
}
}
 
is also wrong. This will break (at runtime) any class which tries to
enumerate this collection through its System.Collections.IEnumerable
interface.

No it won't - it's an explicit interface implementation. Look it up in the
SDK; it's private to the class.

But it is wrong, it should return an IEnumerator, not an IEnumerable.



David Browne said:
KH said:
My previous answer is correct; did you try doing what I said? Listen
closely:

You implemented System.Collections.Generic.IEnumerable<string>

You DID NOT implement System.Collections.IEnumerable

They are NOT the same thing. This is what the compiler is complaining
about.

But

System.Collections.IEnumerable
System.Collections.IEnumerable.GetEnumerator()
{
return null;
}

is also wrong. This will break (at runtime) any class which tries to
enumerate this collection through its System.Collections.IEnumerable
interface.


Instead you should just return IEnumerable<string>.GetEnumerator() since
IEnumerator<string> implements IEnumerator.


System.Collections.IEnumerable
System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

Here's the complete sample.

David


#region Using directives

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

#endregion

namespace Enumerable
{
public class ListBoxTest : IEnumerable<String>,IEnumerable
{
private string[] strings;
private int ctr = 0;
// Enumerable classes can return an enumerator
public IEnumerator<string> GetEnumerator()
{
foreach (string s in strings)
{
yield return s;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}


// initialize the list box with strings
public ListBoxTest(params string[] initialStrings)
{
// allocate space for the strings
strings = new String[8];

// copy the strings passed in to the constructor
foreach (string s in initialStrings)
{
strings[ctr++] = s;
}
}

// add a single string to the end of the list box
public void Add(string theString)
{
strings[ctr] = theString;
ctr++;
}

// allow array-like access
public string this[int index]
{
get
{
if (index < 0 || index >= strings.Length)
{
// handle bad index
}
return strings[index];
}
set
{
strings[index] = value;
}
}

// publish how many strings you hold
public int GetNumEntries()
{
return ctr;
}

}

public class Tester
{
static void Main()
{
// create a new list box and initialize
ListBoxTest lbt =
new ListBoxTest("Hello", "World");

// add a few strings
lbt.Add("Who");
lbt.Add("Is");
lbt.Add("John");
lbt.Add("Galt");

// test the access
string subst = "Universe";
lbt[1] = subst;

// access all the strings
foreach (object s in lbt)
{
Console.WriteLine("Value: {0}", s);
}
}
}
}
 
KH said:
No it won't - it's an explicit interface implementation. Look it up in the
SDK; it's private to the class.

The declaration is fine. You just aren't supposed to
return null;
You need to return a valid System.Collections.IEnumerable. Not null.

You'll notice that the exaple I posted also used explicit interface
implementation.

David
 
Indeed yours does, sorry I didn't read closely. But the explicit
implementation is not visible to users of the class; you could use it
internally, but why would you do that? It's 6 one way 1/2 dozen the other.
 
KH said:
Indeed yours does, sorry I didn't read closely. But the explicit
implementation is not visible to users of the class; you could use it
internally, but why would you do that? It's 6 one way 1/2 dozen the other.

No. An explict interface implementation is public, but only visible after a
cast to the interface.

If a user of the class had cast it to IEnumerable, or (more likely) passed
it to a method which expected IEnumerable then the explicitly implemented
interface method would be visible.

EG

passing the class to this function

void EnumMe(IEnumerable col)
{
foreach (object o in col)
{
Console.WriteLine(o);
}
}

would have resulted in an exception.

David
 
KH said:
Indeed yours does, sorry I didn't read closely. But the explicit
implementation is not visible to users of the class; you could use it
internally, but why would you do that? It's 6 one way 1/2 dozen the other.

You've missed the point of explicit interface implementation. It's
*not* private to the class - it's used by anything which only knows
about the object as an IEnumerable. If I do:

IEnumerable foo = new ListBoxTest();

IEnumerator x = foo.GetEnumerator();

that would return null, which is definitely the *wrong* thing to do.
 
Back
Top