Generics collection class that supports foreach ..


B

Bit Byte

I am writing a generic container class which I want to support the
'foreach' statement.

My class looks something like this:

class MyClass<T> : CollectionBase
{
//Implementation for MyClass<T> goes here ...
// ...

//The next method causes compiler warning
IEnumerator GetEnumerator()
{
return (IEnumerator) new MyClassEnumerator<T>(this) ;
}

private class MyClassEnumerator<T>: IEnumerator
{
private int idx;
private MyClass<T> obj ;

//IEnumerator methods implemented here ...
}
}

The warning is that the method 'hides' the GetEnumerator() in
CollectionBase - and the suggestion is that I use the new keyword. What
gives? I mean I understand when the new keywod is used - but this seems
like too much "hand holding" by the compiler - Unless I am reinventing
the wheel here , and I automatically inherit the enumeration
functionality I desire (i.e. use of the foreach statement)
"automagically" - by dint of the fact that I am inheriting from
CollectionBase?


PS: I'm new to C#, but coming from several years C/C++ background.
 
Ad

Advertisements

A

Adam Clauss

CollectionBase assumes you will be adding items to it's internal List
property. The CollectionBase.GetEnumerator() then iterates across that
list, it does not need any "help" from you to do so. Maybe it is
CollectionBase that you should not be inheriting from. If you are wanting
to implement custom storage of the list, and a custom enumerator, you may
want to try deriving from the generic interfaces instead (such as IList<> if
a "list" adequately represents the collection you are creating).
 
J

Jon Skeet [C# MVP]

The warning is that the method 'hides' the GetEnumerator() in
CollectionBase - and the suggestion is that I use the new keyword. What
gives? I mean I understand when the new keywod is used - but this seems
like too much "hand holding" by the compiler - Unless I am reinventing
the wheel here , and I automatically inherit the enumeration
functionality I desire (i.e. use of the foreach statement)
"automagically" - by dint of the fact that I am inheriting from
CollectionBase?

The compiler doing some hand-holding is entirely appropriate here -
CollectionBase already implement IEnumerable, so the compiler is
telling you that you're hiding an existing method. It's also
effectively informing you that you're *not* overriding the existing
method, but hiding it. If all of that is really what you want to do,
you should use "new" - but in this case, unless you actually want to
provide a completely different IEnumerator semantics, you just don't
want to write the extra code. Note that if you *do* want to provide
different semantics, you'll get different behaviour depending on how
exactly GetEnumerator is called.

Basically, because member hiding is not something you want to do often,
the compiler wants you to explicitly use the "new" modifier when you do
so, just in case you didn't mean to (as here).
 
D

David Browne

Bit Byte said:
I am writing a generic container class which I want to support the
'foreach' statement.

My class looks something like this:

class MyClass<T> : CollectionBase
{
//Implementation for MyClass<T> goes here ...
// ...

//The next method causes compiler warning
IEnumerator GetEnumerator()
{
return (IEnumerator) new MyClassEnumerator<T>(this) ;
}

private class MyClassEnumerator<T>: IEnumerator
{
private int idx;
private MyClass<T> obj ;

//IEnumerator methods implemented here ...
}
}

You don't need to implement GetEnumerator, the base class does that. If you
want to add a typed enumerator declare that you implement IEnumerable<T> and
implement that explicily.

Like this:

class MyClass<T> : CollectionBase, IEnumerable<T>
{

private class MyClassEnumerator : IEnumerator<T>
{
private int idx;
private MyClass<T> obj;

public MyClassEnumerator(MyClass<T> myClass)
{
obj = MyClass;
}

//IEnumerator methods implemented here ...
}

IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return new MyClassEnumerator<T>(this);
}

}

David
 
B

Bit Byte

(continuing with top posting - to maintain readability)
Hi Adam,

If I understand correctly, what you are saying is that I can use the
'foreach' statement when using my generic collection - WITHOUT having to
explicity implement it (by means of an inner class as I did earlier) -
because CollectionBase already provides this?

So I can write something like this (I know I could easily test this
myself - but I am in the middle of porting accross a huge C++ library ..) :

void FooBar()
{
MyClass<string> col = new MyClass<String>() ;
col.Add("hello") ;
col.Add("there") ;
col.Add("again") ;

foreach (string s in col)
Console.Write("{0}\n", s) ;

}

Will the code above work (ignoring any typos etc) ?
 
J

Jim H

Is it also being hidden because he didn't specify public? Since he didn't
specify it defaults to private right? I come from a C++ background as well
and I'm still learning C#.

just asking,
jim
 
Ad

Advertisements

A

Adam Clauss

Bit Byte said:
(continuing with top posting - to maintain readability)

My fault on that, I'm really trying to break that old habit, still
occasionally forget :)
If I understand correctly, what you are saying is that I can use the
'foreach' statement when using my generic collection - WITHOUT having to
explicity implement it (by means of an inner class as I did earlier) -
because CollectionBase already provides this?

Correct, CollectionBase already implements a default Enumerator(). Only
reason you would need to provide your own is if you know it is doing
something wrong. And if you DO need to provide your own... I would really
recommend not using CollectionBase (see Jon's discussion about hiding
members using 'new'). That can lead to bad unintended consequences.
So I can write something like this (I know I could easily test this
myself - but I am in the middle of porting accross a huge C++ library ..)
:

void FooBar()
{
MyClass<string> col = new MyClass<String>() ;
col.Add("hello") ;
col.Add("there") ;
col.Add("again") ;

foreach (string s in col)
Console.Write("{0}\n", s) ;

}

Will the code above work (ignoring any typos etc) ?

I don't see any immediate reason it wouldn't.

I also want to express concern that you are using generics, but using the
non-generic CollectionBase. Despite you doing
MyClass<string> col = new MyClass<String>() ;

The Add method is simply inherited from CollectionBase, correct? Thus, it
simply takes an object as a parameter, not a string. So this would be
valid, and run successfully:
col.Add(2);

But then when you get down to your foreach() loop, it would throw an
InvalidCastException (I think that's the right one... an exception of some
form anyway) because the integer 2 is not of type string.

You might consider instead inheriting from List<T> and overriding methods as
necessary for whatever custom implementation you are doing. This again,
already implements the enumerator for you, and also provides the type safety
of ensuring only items of the generic type (strings in this example) are
added to the list.

Hope that helps, let me know if you have any other questions.
 
A

Adam Clauss

Adam Clauss said:
I don't see any immediate reason it wouldn't.

I also want to express concern that you are using generics, but using the
non-generic CollectionBase. Despite you doing
MyClass<string> col = new MyClass<String>() ;

The Add method is simply inherited from CollectionBase, correct? Thus, it
simply takes an object as a parameter, not a string. So this would be
valid, and run successfully:
col.Add(2);

Correction here - I made a bad assumption. I thought Add was part of
CollectionBase.
The whole discussion of whether or not the built-in enumeration will work
for you depends on your implementation Add. If you are putting them in
CollectionBase's own List, then the built-in enumeration works fine. If you
are not... then you have no need to use CollectionBase :)
 
J

Jon Skeet [C# MVP]

Jim H said:
Is it also being hidden because he didn't specify public? Since he didn't
specify it defaults to private right? I come from a C++ background as well
and I'm still learning C#.

It does default to private, but that's got nothing to do with it hiding
the existing method. It's not that the new version is *being* hidden,
it's that it *is* hiding the existing method.
 
J

Joanna Carter [TeamB]

"Bit Byte" <[email protected]> a écrit dans le message de (e-mail address removed)...

|I am writing a generic container class which I want to support the
| 'foreach' statement.
|
| My class looks something like this:
|
| class MyClass<T> : CollectionBase
| {
| //Implementation for MyClass<T> goes here ...
| // ...

Can I ask why you are not using System.Collections.Generic.List<T> ? This
already supports IEnumerable and IEnumerable<T>, thus making it suitable for
use with the foreach construct. Or do you want a custom enumerator ?

{
List<Customer> customers = ......

foreach (Customer c in customers)
......

}

Joanna
 
Ad

Advertisements


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