tags:

views:

1335

answers:

7

I have some code where I'm returning an array of objects.

Here's a simplified example:

string[] GetTheStuff() {
    List<string> s = null;
    if( somePredicate() ) {
        s = new List<string>(); // imagine we load some data or something
    }
    return (s == null) ? 
        new string[0] :
        s.ToArray();
}

The question is, how expensive is the new string[0] ?
Should I just return null and make the caller accept null as a valid way of indicating "nothing was found"?

NB: This is being called in a loop which gets run hundreds and hundreds of times, so it's one of the few cases where I think this kind of optimiziation is not actually 'premature'.

PS: And even if it was premature, I'd still like to know how it works :-)

Update:

Initially when I asked if it used any space, I was thinking of things from the 'C/C++' point of view, kind of like how in C, writing char a[5]; will allocate 5 bytes of space on the stack, and char b[0]; will allocate 0 bytes.

I realise this is not a good fit for the .NET world, but I was curious if this was something that the compiler or CLR would detect and optimize out, as a non-resizeable array of size zero really shouldn't (as far as I can see?) require any storage space.

+3  A: 

I would guess that an empty array uses only the space needed to allocate the object pointer itself.

From memory the API guidelines say that you should always return an empty array from a method that returns an array rather than returning null, so I'd leave your code the way it is regardless. That way the caller knows he's guaranteed to get an array (even an empty one) and need not check for null with each call.

Edit: A link about returning empty arrays:

http://wesnerm.blogs.com/net_undocumented/2004/02/empty_arrays.html

Matt Hamilton
+3  A: 

Declared arrays will always have to contain the following information:

  • Rank (number of dimensions)
  • Type to be contained
  • Length of each dimension

This would most likely be trivial, but for higher numbers of dimensions and higher lengths it will have a performance impact on loops.

As for return types, I agree that an empty array should be returned instead of null.

More information here: Array Types in .NET

Jon Limjap
I would expect the rank and type to be contained to be part of the type information itself (i.e. part of the normal 8 byte overhead). I haven't checked, mind you. There's the lower bound to consider, but I believe that would be dealt with as a different type (array rather than vector in CLR terms).
Jon Skeet
A: 

If I understand correctly, a small amount of memory will be allocated for the string arrays. You code essentially requires a generic list to be created anyway, so why not just return that?

[EDIT]Removed the version of the code that returned a null value. The other answers advising against null return values in this circumstance appear to be the better advice[/EDIT]

List<string> GetTheStuff()
{
   List<string> s = new List<string();
   if (somePredicarte())
   {
      // more code
   }
   return s;
}
Dr8k
For trivial operations, generic lists are fine, but for high numbers of loops, they will consistently become slow. Also, behind the scenes a generic list IS indeed an array. Use reflector and check it out. ;)
Jon Limjap
@Jon: Dr8k' point was that there already _is_ a generic List so his argument remains valid.
VVS
Thanks David. You are correct about my point. If it is neccesary to return an array value for performance reasons, then internally the method should also use an array value.
Dr8k
The thing is, I only need to build a list if the predicate returns true. 9 times out of 10 it won't, so I want to avoid constructing a List if I can. The question really should be, how much more expensive is it to create an empty list rather than an empty array
Orion Edwards
+30  A: 

Even if it's being called "hundreds and hundreds" of times, I'd say it's a premature optimization. If the result is clearer as an empty array, use that.

Now for the actual answer: yes, an empty array takes some memory. It has the normal object overhead (8 bytes on x86, I believe) and 4 bytes for the count. I don't know whether there's anything beyond that, but it's not entirely free. (It is incredibly cheap though...)

Fortunately, there's an optimization you can make without compromising the API itself: have a "constant" of an empty array. I've made another small change to make the code clearer, if you'll permit...

private static readonly string[] EmptyStringArray = new string[0];

string[] GetTheStuff() {
    if( somePredicate() ) {
        List<string> s = new List<string>(); 
        // imagine we load some data or something
        return s.ToArray();
    } else {
        return EmptyStringArray;
    }
}

If you find yourself needing this frequently, you could even create a generic class with a static member to return an empty array of the right type. The way .NET generics work makes this trivial:

public static class Arrays<T> {
    public static readonly Empty = new T[0];
}

(You could wrap it in a property, of course.)

Then just use: Arrays<string>.Empty;

EDIT: I've just remembered Eric Lippert's post on arrays. Are you sure that an array is the most appropriate type to return?

Jon Skeet
Doh. I've actually used that 'return a constant' trick dozens of times before. I feel dumb for not having thought of that :-)
Orion Edwards
Good trick. Similar to String.Empty :)
Thomas Bratt
Beware of the static readonly array as only the array is readonly. The contents are still mutable. So someone could take the empty array you return and stick something in it, thereby making you return non-empty arrays instead.
Jeff Yates
Ah, your edit covers that with Eric Lippert's post (exactly the post that raised it in my mind). :)
Jeff Yates
ffpf: How would anyone stick something in an empty array? Yes, only the array reference is read-only - but the array is still of a fixed size, 0.
Jon Skeet
I think ffpf doesn't know that in Java and .NET the arrays are fixed size and cannot be enlarged or trimmed down. Only their contents may be altered but this one is 'very' empty.
Andrei Rinea
+2  A: 

This is not a direct answer to your question.

Read why arrays are considered somewhat harmful. I would suggest you to return an IList<string> in this case and restructure the code a little bit:

IList<string> GetTheStuff() {
    List<string> s = new List<string>();
    if( somePredicate() ) {
        // imagine we load some data or something
    }
    return s;
}

In this way the caller doesn't have to care about empty return values.


EDIT: If the returned list should not be editable you can wrap the List inside a ReadOnlyCollection. Simply change the last line to. I also would consider this best practice.

    return new ReadOnlyCollection(s);
VVS
unfortunately a List gives the caller the possibility of changing the item count. An array assures an immutable length. I would advise against this if it should not be added to/removed from. If its just to enumerate, the best bet is an enumerable.
mattlant
Nice article link!
Orion Edwards
+2  A: 

Yes, as others have said, the empty array takes up a few bytes for the object header and the length field.

But if you're worried about performance you're focusing on the wrong branch of execution in this method. I'd be much more concerned about the ToArray call on the populated list which will result in a memory allocation equal to its internal size and a memory copy of the contents of the list into it.

If you really want to improve performance then (if possible) return the list directly by making the return type one of: List<T>, IList<T>, ICollection<T>, IEnumerable<T> depending on what facilities you need from it (note that less specific is better in the general case).

Greg Beech
+3  A: 

Others have answered your question nicely. So just a simple point to make...

I'd avoid returning an array (unless you can't). Stick with IEnumerable and then you can use Enumerable.Empty<T>() from the LINQ APIs. Obviously Microsoft have optimised this scenario for you.

IEnumerable<string> GetTheStuff()
{
    List<string> s = null;
    if (somePredicate())
    {
        var stuff = new List<string>();
        // load data
        return stuff;
    }

    return Enumerable.Empty<string>();
}
Drew Noakes