views:

115

answers:

3

In C#, what's the most elegant way to create an array of objects, from an enumerator of objects? e.g. in this case I have an enumerator that can return byte's, so I want to convert this to byte[].

EDIT: Code that creates the enumerator:

IEnumerator<byte> enumurator = updDnsPacket.GetEnumerator();
+8  A: 

Assuming you have an IEnumerable<T>, you can use the Enumerable.ToArray extension method:

IEnumerable<byte> udpDnsPacket = /*...*/;

byte[] result = udpDnsPacket.ToArray();
dtb
Gotta love LINQ!
Nathan Taylor
I don't see a "ToArray()" in the intellisense? Is this one of these cases I have to add a using statement to get some extra methods?
Greg
@Greg: Yes. You need to add `using System.Linq;` in order to make the `Enumerable` class visible.
dtb
got it thanks - it doesn't add any real overhead behind the scenes re pulling in the Linq libraries?
Greg
Nope, if you are already using 3.5 or greater, then you already have the Linq extensions so, there is no overhead in "pulling" them in.
Brian Genisio
Is it just me or is the question clearly NOT about an IEnumerable but about an IEnumerator?
Ronald Wildenberg
@Ronald Wildenberg: It is somewhat unusual to have an IEnumerator and not an IEnumerable. The original question was not that clear about this as it is now. Also, the OP's `updDnsPacket` object looks quite likely to implement IEnumerable, so why not take advantage of that instead of following the false assumption that the only way to create an array from an IEnumerable is to get the enumerator and continue from there.
dtb
@dtb: The comment wasn't really addressed at you but more at everyone who voted you up :) Reading the inital question I understand the assumption.
Ronald Wildenberg
+7  A: 

OK, So, assuming that you have an actual enumerator (IEnumerator<byte>), you can use a while loop:

var list = new List<byte>();
while(enumerator.MoveNext())
  list.Add(enumerator.Current);
var array = list.ToArray();

In reality, I'd prefer to turn the IEnumerator<T> to an IEnumerable<T>:

public static class EnumeratorExtensions
{
    public static IEnumerable<T> ToEnumerable<T>(this IEnumerator<T> enumerator)
    {
      while(enumerator.MoveNext())
          yield return enumerator.Current;
    }
}

Then, you can get the array:

var array = enumerator.ToEnumerable().ToArray();

Of course, all this assumes you are using .Net 3.5 or greater.

Brian Genisio
The foreach construct actually doesn't care if you're an IEnumerable as long as you have GetEnumerator declared. I only mention this because you don't. =)
Marc
@Marc: I don't see how that is relevant to my answer? I am not using foreach here.
Brian Genisio
@Brian, as a (imo) cleaner alternative to `while`
Marc
@Marc: But you can't do foreach on an IEnumerator. You can only do it on an IEnumerable. My while loop translates an IEnumerator to an IEnumerable.
Brian Genisio
@Brian Genisio: Which brings me back to my original comment. The poster says he has a class that has a method named `GetEnumerator` that returns an IEnumerator. This is enough for `foreach` to do it's work. `foreach(var b in updDnsPacket) list.Add(b);` That's all I was getting at, sorry for being so vague.
Marc
@marc Ok, I get what you are saying now. I wrote my answer before he showed the example code, so I missed it. I still prefer ToEnumerable().ToArray() approach for his situation though.
Brian Genisio
+1  A: 

Since you have an IEnumerator<byte> and not an IEnumerable<byte>, you cannot use Linq's ToArray method. ToArray is an extension method on IEnumerable<T>, not on IEnumerator<T>.

I'd suggest writing an extension method similar to Enumerable.ToArray but then for the purpose of creating an array of your enumerator:

public T[] ToArray<T>(this IEnumerator<T> source)
{
    T[] array = null;
    int length = 0;
    T t;
    while (source.MoveNext())
    {
        t = source.Current();
        if (array == null)
        {
            array = new T[4];
        }
        else if (array.Length == length)
        {
            T[] destinationArray = new T[length * 2];
            Array.Copy(array, 0, destinationArray, 0, length);
            array = destinationArray;
        }
        array[length] = t;
        length++;
    }
    if (array.Length == length)
    {
        return array;
    }
    T[] destinationArray = new T[array.Length];
    Array.Copy(array, 0, destinationArray, 0, array.Length);
    return destinationArray;
}

What happens is that you iterate your enumerator item by item and add them to an array that is gradually increasing in size.

Ronald Wildenberg
got to run - will test the Linq solution when I'm back to see if it works (the method appears) - why do you say I cannot use the Linq ToArray method?
Greg
He means you can call ToArray() on IEnumerable (so on udpDnsPacket in your case), but not on IEnumerator
digEmAll