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;
}
}
1 Answer 1
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 Array
s 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 null
ing 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.
-
\$\begingroup\$ "The conventional wisdom says resizing
Array
is less performant than resizing aList
." That doesn't make much sense. ResizingList
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 thanList
. \$\endgroup\$svick– svick2013年10月20日 23:16:22 +00:00Commented Oct 20, 2013 at 23:16 -
\$\begingroup\$ "Should ResizableArray inherit Array?" No, you can't do that. It wouldn't compile. \$\endgroup\$svick– svick2013年10月20日 23:18:11 +00:00Commented Oct 20, 2013 at 23:18
-
\$\begingroup\$ Huh. One more reason to not use an Array. \$\endgroup\$radarbob– radarbob2013年10月21日 00:20:39 +00:00Commented Oct 21, 2013 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\$Oleg Vazhnev– Oleg Vazhnev2013年10月21日 06:13:53 +00:00Commented Oct 21, 2013 at 6:13
-
\$\begingroup\$ Beautiful answer! +1 \$\endgroup\$Mathieu Guindon– Mathieu Guindon2013年10月25日 03:05:11 +00:00Commented Oct 25, 2013 at 3:05
foreach
? What's the intended usage ofRemoveRange()
? Why is thereClear()
? \$\endgroup\$List<>
which already exists and is very cheap. Also, you should implementIEnumerable<T>
\$\endgroup\$