views:

264

answers:

8

I just realize that maybe I was mistaken all the time in exposing T[] to my views, instead of IEnumerable<T>.

Usually, for this kind of code:

foreach (var item in items) {}

item should be T[] or IEnumerable<T>?

Than, if I need to get the count of the items, would the Array.Count be faster over the IEnumerable<T>.Count()?

+4  A: 

It's partially inconsequential, but standard theory would dictate "Program against an interface, not an implementation". With the interface model you can change the actual datatype being passed without effecting the caller as long as it conforms to the same interface.

The contrast to that is that you might have a reason for exposing an array specifically and in which case would want to express that.

For your example I think IEnumerable<T> would be desirable. It's also worthy to note that for testing purposes using an interface could reduce the amount of headache you would incur if you had particular classes you would have to re-create all the time, collections aren't as bad generally, but having an interface contract you can mock easily is very nice.

Added for edit:
This is more inconsequential because the underlying datatype is what will implement the Count() method, for an array it should access the known length, I would not worry about any perceived overhead of the method. See Jon Skeet's answer for an explanation of the Count() implementation.

Quintin Robinson
So, generally, I shouldn't use `T[]`, right? when *should* I use `T[]`?
stacker
@Quintin, C# arrays are not immutable.
Matthew Flaschen
@Matthew Flaschen That's one of the reasons why I said there are caveats, I didn't want to break it down, some perceive them to be immutable although you can modify the contents of the indices.
Quintin Robinson
@stacker Typically when you want to represent a perceived readonly collection or need the memory to be sequential. Of course there are caveats to this.
Quintin Robinson
@Quintin if it's immutable collection (perceived readonly), that need to be iterated (I didn't get what's `the memory to be sequential` mean), didn't IEnumerable<T> better? This is exactly the case in my views.
stacker
@stacker Yes, like I said, for your purposes and probably for most general cases, `IEnumerable<T>` is better suited.
Quintin Robinson
@stacker, you're correct. If you just want to access (not modify) the elements in consecutive order, `IEnumerable` is the correct choice. An array is appropriate when you want a fixed size collection and the ability to access and modify any element by index - [random access](http://en.wikipedia.org/wiki/Random_access).
Matthew Flaschen
@Quintin Through, is there any case that I want to `T[]`? I mean if I need it to be indexed, I can use `IList<T>`.
stacker
@Matthew thanks for the link. I think you answered the question above.
stacker
+3  A: 

T[] (one sized, zero based) also implements ICollection<T> and IList<T> with IEnumerable<T>.

Therefore if you want lesser coupling in your application IEnumerable<T> is preferable. Unless you want indexed access inside foreach.

Andrei Taptunov
+1  A: 

Your gut feeling is correct, if all the view cares about, or should care about, is having an enumerable, that's all it should demand in its interfaces.

Cirdec
+2  A: 

Since Array class implements the System.Collections.Generic.IList<T>, System.Collections.Generic.ICollection<T>, and System.Collections.Generic.IEnumerable<T> generic interfaces, I would use IEnumerable, unless you need to use these interfaces.

http://msdn.microsoft.com/en-us/library/system.array.aspx

negative
+11  A: 

IEnumerable<T> is generally a better choice here, for the reasons listed elsewhere. However, I want to bring up one point about Count(). Quintin is incorrect when he says that the type itself implements Count(). It's actually implemented in Enumerable.Count() as an extension method, which means other types don't get to override it to provide more efficient implementations.

By default, Count() has to iterate over the whole sequence to count the items. However, it does know about ICollection<T> and ICollection, and is optimised for those cases. (In .NET 3.5 IIRC it's only optimised for ICollection<T>.) Now the array does implement that, so Enumerable.Count() defers to ICollection<T>.Count and avoids iterating over the whole sequence. It's still going to be slightly slower than calling Length directly, because Count() has to discover that it implements ICollection<T> to start with - but at least it's still O(1).

The same kind of thing is true for performance in general: the JITted code may well be somewhat tighter when iterating over an array rather than a general sequence. You'd basically be giving the JIT more information to play with, and even the C# compiler itself treats arrays differently for iteration (using the indexer directly).

However, these performance differences are going to be inconsequential for most applications - I'd definitely go with the more general interface until I had good reason not to.

Jon Skeet
Thanks for your answer. Can I assume that using `IEnumerable<T>` and `Count()` is more efficient, then constructing an `T[]` just for getting the `Length` property?
stacker
@stacker: Creating a copy of a collection into an array just to get its length would certainly be less efficient than just calling `Count()`, yes.
Jon Skeet
How often do you need to get the number of elements? If you have to re-enumerate every element to get the `Count`, it could get expensive.
Gabe
@Gabe only once. I'm chancing it in a variable.
stacker
@stacker: IEnumerables can be infinite in theory, therefore they have no Count. Using an array you have Length. If you find yourself needing a count, use ICollection instead of IEnumerable.
Johannes Rudolph
@Johannes is using `ICollection` better than just use `count()`?
stacker
@stacker: If you use `Enumerable.Count`, that will notice if your collection implements `ICollection<T>` and use the `Count` property automatically. It's not clear exactly what you're really getting at here.
Jon Skeet
@Jon: Who is you, me?
Johannes Rudolph
@Johannes: I mean it's not quite clear what stacker is trying to achieve, or what he's concerned about.
Jon Skeet
@Jon: Ok, just wanted to make sure. And I couldn't resist the poetic tone of these words ;-)
Johannes Rudolph
@Jon thanks for the answer. I just want to make sure that it's better to use IEnumerable<T> instead T[], instead the Model's, View Model's and Repositories.
stacker
A: 

Here is a interesting test for this, please help to improve it.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace ArrayTest
{
    public class Foo
    {
        public string Bar1 { get; set; }
        public string Bar2 { get; set; }
        public string Bar3 { get; set; }
        public string Bar4 { get; set; }
        public string Bar5 { get; set; }
    }

    class Program
    {
        const int Size = 10000000;
        static readonly List<string> Output = new List<string>();
        static readonly List<string> Print = new List<string>();

        static void Main()
        {
            var foo1 = GetFoo1();
            var foo2 = GetFoo2();

            UseArray(foo1);
            UseIEnumerable(foo2);

            // Get random element from the list. Just for avoiding compiler optimization.
            int i = new Random().Next(Output.Count);
            Console.WriteLine(Output[i]);

            foreach (string o in Print)
            {
                Console.WriteLine(o);
            }
        }

        private static void WriteLine(string x)
        {
            Output.Add(x);
        }

        private static Foo[] GetFoo1()
        {
            Stopwatch sw = Stopwatch.StartNew();
            var foo1 = new Foo[Size];
            for (int i = 0; i < Size; i += 1)
            {
                var foo = new Foo { Bar1 = "Bar1", Bar2 = "Bar2", Bar3 = "Bar3", Bar4 = "Bar4", Bar5 = "Bar5" };
                foo1[i] = foo;
            }
            sw.Stop();
            Print.Add(string.Format("CreateArray: {0}", sw.ElapsedMilliseconds));
            return foo1;
        }

        private static IEnumerable<Foo> GetFoo2()
        {
            Stopwatch sw = Stopwatch.StartNew();
            for (int i = 0; i < Size; i += 1)
            {
                var foo = new Foo {Bar1 = "Bar1", Bar2 = "Bar2", Bar3 = "Bar3", Bar4 = "Bar4", Bar5 = "Bar5"};
                yield return foo;
            }
            sw.Stop();
            Print.Add(string.Format("CreateIEnumerable: {0}", sw.ElapsedMilliseconds));
        }

        static void UseArray<T>(T[] values)
        {
            Stopwatch sw = Stopwatch.StartNew();
            var count = values.Length;
            foreach (object o in values)
            {
                var x = (Foo)o;
                WriteLine(x.Bar1);
            }
            sw.Stop();
            Print.Add(string.Format("UseArray: {0}, count: {1}", sw.ElapsedMilliseconds, count));
        }

        static void UseIEnumerable<T>(IEnumerable<T> values)
        {
            Stopwatch sw = Stopwatch.StartNew();
            var count = values.Count();
            foreach (object o in values)
            {
                var x = (Foo)o;
                WriteLine(x.Bar1);
            }
            sw.Stop();
            Print.Add(string.Format("UseIEnumerable: {0}, count: {1}", sw.ElapsedMilliseconds, count));
        }
    }
}
stacker
It's interesting that sometimes the array is faster and sometimes the enumerable is faster. My guess is that this is because you have `Console.Writeline` in your loop. If you remove the I/O, you'll probably see the array is always faster.
Gabe
A: 

What is it logically (conceptually) from the outside?

If it's an array, then return the array. If the only point is to enumerate, then return IEnumerable. Otherwise IList or ICollection may be the way to go.

If you want to offer lots of functionality but not allow it to be modified, then perhaps use a List internally and return the ReadonlyList returned from it's .AsReadOnly() method.

Jon Hanna
A: 

Given that changing the code from an array to IEnumerable at a later date is easy, but changing it the other way is not, I would go with a IEnumerable until you know you need the small spead benfit of return an array.

Ian Ringrose