Reputation: 331490
So for a type like:
CoolCollection<T>
you could have:
foreach (T item in coolCollection)
{
...
}
foreach (CoolNode node in coolCollection)
{
...
}
If this isn't possible, maybe like foreach2, or some other way to iterate. Often times, I would really like more than 1 way of iterating on a type.
EDIT: Sorry if it wasn't clear. Basically CoolNode is a node that makes CoolCollection. CoolNode has a property called value to return T, but I need another iterator to return only CoolNodes.
EDIT2: I can't do coolCollection.Something to iterate, because CoolNodes are connected via a property called Next, like a LinkedList. So I need to implement 2 iterators.
Upvotes: 1
Views: 994
Reputation: 564871
Just make CoolCollection<T>
explicitly implement IEnumerable<CoolNode<T>>
as well as IEnumerable<T>
. (I'm guessing it's really CoolNode<T>
, but if not, just take the extra <T>
out everywhere.)
This will let you iterate in both manners, although you'll need a cast.
To do this, you'd need something like:
class CoolCollection<T> : ICollection<T>, IEnumerable<CoolNode<T>>
{
IEnumerator<CoolNode<T>> IEnumerable<CoolNode<T>>.GetEnumerator()
{
///...Do work here...
}
IEnumerator<T> GetEnumerator()
{
///...Do work here...
}
}
Using this would be like so:
foreach (T item in coolCollection)
{
...
}
foreach (CoolNode<T> node in (IEnumerable<CoolNode<T>>)coolCollection)
{
...
}
The other option would be to expose a property for the "nodes", so you could do:
foreach(var nodes in coolCollection.Nodes)
{ ... }
To implement this, you'd change things around a little bit. You'd need to make a private class that implemented the enumerator... something like:
class CoolCollection<T> : ICollection<T>
{
private List<CoolNode<T>> nodes;
IEnumerable<CoolNode<T>> Nodes
{
get
{
foreach(var node in this.nodes) { yield return node; }
}
}
}
Upvotes: 3
Reputation: 131182
No, you can't do that. You can not overload your default iterator.
Imagine if you could overload your default iterator.
What would this do? foreach (object o in foo)
, there would be no logical way to choose the right iterator.
What you can do is have a second method named ForEach2 that iterates through your collection in a different way. Or you could explicitly implement an interface. Or you could use Linq composition for this kind of stuff.
From a class design perspective:
interface IBar {
IEnumerator<string> GetEnumerator();
}
class Foo : IBar, IEnumerable<int> {
// Very bad, risky code. Enumerator implementations, should
// line up in your class design.
public IEnumerator<int> GetEnumerator()
{
yield return 1;
yield return 2;
yield return 3;
yield return 4;
}
IEnumerator<string> IBar.GetEnumerator()
{
yield return "hello";
}
// must be IEnumerable if you want to support foreach
public IEnumerable<string> AnotherIterator
{
get {
yield return "hello2";
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
LINQ extensions for EachPair
struct Pair<T> {
public T First;
public T Second;
}
static class LinqExtension {
public static IEnumerable<Pair<T>> EachPair<T>(this IEnumerable<T> input) {
T first = default(T);
bool gotFirst = false;
foreach (var item in input)
{
if (!gotFirst)
{
first = item;
gotFirst = true;
}
else {
yield return new Pair<T>() { First = first, Second = item };
gotFirst = false;
}
}
}
}
Test code:
class Program
{
static void Main(string[] args)
{
var foo = new Foo();
foreach (int number in foo)
{
Console.WriteLine(number);
}
// LINQ composition - a good trick where you want
// another way to iterate through the same data
foreach (var pair in foo.EachPair())
{
Console.WriteLine("got pair {0} {1}", pair.First, pair.Second);
}
// This is a bad and dangerous practice.
// All default enumerators should be the same, otherwise
// people will get confused.
foreach (string str in (IBar)foo)
{
Console.WriteLine(str);
}
// Another possible way, which can be used to iterate through
// a portion of your collection eg. Dictionary.Keys
foreach (string str in foo.AnotherIterator)
{
Console.WriteLine(str);
}
}
Upvotes: 1
Reputation: 46546
Take a look at the iterindex
snippet. In your class, type iterindex
and hit [TAB]
. It will help you implement a "Named Iterator and Indexer" pattern.
The result can by used like this:
foreach (var e in myTree.DepthFirstView) // supports iteration
{
if (e == myTree.DepthFirstView[2]) // supports indexing
{
// ...
}
}
(I wrote this snippet, but I suspect it has never been put to good use. Ever.)
Upvotes: 0
Reputation: 2688
If CoolCollection implements IEnumerable you can write:
foreach (var item in coolCollection)
{
...
}
or if T is CoolNode
foreach (CoolNode node in coolCollection)
{
...
}
If you need somehow transform each item to your type, you can use Linq Select operator:
foreach (CoolNode node in coolCollection.Select(item => ConvertToNode(item))
{
...
}
Upvotes: 0
Reputation: 7491
If I understand the question correctly...
You could do it similar to the some of the other collection objects do it:
for example:
foreach (int key in IDictionary.Keys)
{
}
foreach (object value in IDictionary.Values)
{
}
But I don't think there is a way to do exactly the way you have it written...
Upvotes: 2