1
\$\begingroup\$

I need array to store elements, but I don't want to make memory allocations at runtime. So I reuse elements. Some fields are never changes so as I reuse elements I need to assign those fields just once.

This is what I wrote, only sceleton, need to add "errors test" etc, but enough to demonstrate the idea:

public sealed class ResizeableArray<T> where T : class, new()
{

    // made public to use in "foreach", but better to be private
    public T[] array;
    public int Count { get; private set; }

    public ResizeableArray(uint maxLength = 128)
    {
        if (maxLength <= 0) throw new ArgumentOutOfRangeException("maxLength");
        array = new T[maxLength];
        for (int i = 0; i < maxLength; i++)
        {
            array[i] = new T();
        }
        Count = 0;
    }

    public T this[int key]
    {
        get { return array[key]; }
    }

    public void Clear()
    {
        Count = 0;
    }

    public T Add()
    {
        return array[Count++];
    }

    public void RemoveRange(int index, int count)
    {
        var newSize = Count - count;
        for (int i = index; i < newSize; i++)
        {
            array[i] = array[i + count];
        }
        Count = newSize;
    }

    public void RemoveAt(int index)
    {
        throw new NotImplementedException();
    }


}

Usage is simple, for example

var element = array.Add();
// configure, assign only "mutable" fields
element.field1 = value1;
element.field2 = value2;

What do you think? Is it good class or you can suggest something better?

upd posting final version I use in production. I think this class can be usefull if you constantly need to reconfigure some array. It's probably will be faster no to allocate new object over and over (but need to measure). Also it's probably less error prone to have always the same instance of the object.

public sealed class ResizeableArray<T> where T : class, new()
{
    private T[] array;
    public int Count { get; private set; }

    public ResizeableArray(uint maxLength = 128)
    {
        if (maxLength <= 0) throw new ArgumentOutOfRangeException("maxLength");
        array = new T[maxLength];
        for (int i = 0; i < maxLength; i++)
        {
            array[i] = new T();
        }
        MaxLength = maxLength;
        Count = 0;
    }

    public uint MaxLength { get; private set; }

    public T this[int key]
    {
        get { return array[key]; }
    }

    public void Clear()
    {
        Count = 0;
    }

    public T Add()
    {
        return array[Count++];
    }

    // sloooow. don't use it
    public T InsertAt0()
    {
        var zeroOrder = array[Count];
        for (int i = Count; i > 0; i--)
        {
            array[i] = array[i - 1];
        }
        array[0] = zeroOrder;
        return zeroOrder;
    }

}
\$\endgroup\$
9
  • \$\begingroup\$ Array got a storage limit which you defined, So in the Add function what happens if you call that & count value is same as array's last accessible index?. \$\endgroup\$ Oct 20 '13 at 13:20
  • \$\begingroup\$ Are you sure you really can't afford those allocations? Allocations are extremely cheap in .Net and deallocations can be fairly efficient too. \$\endgroup\$
    – svick
    Oct 20 '13 at 13:38
  • \$\begingroup\$ Also, could you explain how exactly are you going to use it? Why the support for foreach? What's the intended usage of RemoveRange()? Why is there Clear()? \$\endgroup\$
    – svick
    Oct 20 '13 at 13:41
  • \$\begingroup\$ i don't call that way. my question is about idea in general not about "border tests" (which I will add later) \$\endgroup\$ Oct 20 '13 at 13:43
  • 1
    \$\begingroup\$ Also, it sounds like what you want is a List<> which already exists and is very cheap. Also, you should implement IEnumerable<T> \$\endgroup\$ Oct 20 '13 at 16:37
5
\$\begingroup\$

my question is about idea in general

It's flawed, in several ways.

Premature Optimization

The code smell I get is that the whole point is avoiding instantiating and then disposing Array elements. Reasons we stay away from optimizing up front

  • We end up spending too much time on it
  • Corrupting our business design for the sake of something that we don't even know is an issue.
  • We don't know yet where and how much actual performance is affected.
  • We cannot know if our up front optimization is in fact better than what we would have done; it could be worse!

Resizing?

I thought this is what we're trying to avoid.

Where is the "resizing"? I assume it will be in Add() and Remove(). If there is no resizing - the size is fixed at instantiation, then the class name is wrong.

The conventional wisdom says resizing Array is less performant than resizing a List. In fact the C#/.NET team went to great lengths to make sure List automatic resizing performs well. Do you have the time and other resources to make your home-spun Array resizing worth not using what the .NET framework already gives you?

Wrong Perspective

I see the client having to write his code in terms of Arrays and array elements when it should be in terms of your business objects - TradeOrder, for example.

The structure - the array - should not be the emphasis in your design. If you keep going down this road you are in for ugly maintenance problems over time.

Give the business classes "collection friendly" capabilities

I imagine sorting, searching, preventing duplicates, uniqueness, etc. might be important qualities when we make a collection of things. So TradeOrder should implement IEquatable and IComparable.

ResizeableArray, or any other TradeOrder user's code, should not be doing this:

if (trade1.stockName == trade2.stockName && trade1.shares == trade2.shares && ...)

Client code should be able to do this:

if (trade1.Equals(trade2))

Should ResizableArray inherit Array?

Array already implements foreach, for example. You'll get all the built-in goodness and it will be exposed at the 'class level' to the client. There's lots of nifty search and sort methods that take advantage of ICompareable implementation.

Some fields are never changes so as I reuse elements I need to assign those fields just once

Memory Leaks

As ResizableArray gets used it will have empty and occupied elements scattered throughout the Array. You will end up writing code to scan the array for every Add(). Otherwise you'll be adding new elements when there are empty elements available.

It looks like we leave TradeOrder objects in unused array elements. This is the case at instantiation, clearly. Also removing things involves Count but not nulling the reference there. I guarantee you'll be spending lots of extra code and lots of debugging time trying to keep the Count in synch with the actual active objects.

We don't know what an empty element is

The Count is being incremented/decremented but we're leaving objects in place. I'm assuming that at some point we're done with a given object, in which case it should be disposed of. Besides the memory issue, how do we know what array elements are in use and which ones we can over-write?

// made public to use in "foreach", but better to be private
public T[] array;

NO.

ResizeableArray should implement IEnumerable.

You are forcing client code to be written like this:

foreach (var trade in tradeOrders.array)

When they should write:

foreach (var trade in tradeOrders)

This is a violation of OO principles. Single Responsibility, Law of Demeter (least knowledge), Encapsulation ... a discussion for another time.

As a practical matter do not force the client to have to know how to manipulate the class properties. ResizableArray should know how to iterate itself.

The client should not tell the ResizeableArray object to resize, or how.

\$\endgroup\$
5
  • \$\begingroup\$ “The conventional wisdom says resizing Array is less performant than resizing a List.” That doesn't make much sense. Resizing List is implemented as resizing an array. Resizing array manually would be reinventing the wheel, but I don't see any reason why would it be any slower than List. \$\endgroup\$
    – svick
    Oct 20 '13 at 23:16
  • \$\begingroup\$ “Should ResizableArray inherit Array?” No, you can't do that. It wouldn't compile. \$\endgroup\$
    – svick
    Oct 20 '13 at 23:18
  • \$\begingroup\$ Huh. One more reason to not use an Array. \$\endgroup\$
    – radarbob
    Oct 21 '13 at 0:20
  • \$\begingroup\$ "I'm assuming that at some point we're done with a given object, in which case it should be disposed of." - no, we never done with object. once object is allocated we never destroy it, only reuse. The main idea - objects allocations at runtime must be avoided as expensive operation. I also tend to avoid using Virtual function and of course OOP. Well c# is not right language for low-latency programming, but i need simple thing - fast array with reusable objects without runtime allocations. \$\endgroup\$ Oct 21 '13 at 6:13
  • \$\begingroup\$ Beautiful answer! +1 \$\endgroup\$ Oct 25 '13 at 3:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.