It looks like you have found the best possible solution to every aspect of my
original question. I am assuming that generics are equivalent to C++ templates
so that this solution could be adapted to become a generic solution. I am
assuming by what you said that C# does not have the equivalent the [inline]
keyword.
Peter said:
It also must dynamically re-allocate when it needs to grow in size.
I do not vouch for the following: I wrote it off the top of my head, so
the syntax may not even be 100%, but to give you an idea:
public class MyValueTypeVector
{
private MyValueType[] _vector;
private int _upperBound;
public MyValudTypeVector() : this(10) { }
public MyValueTypeVector(int initialCapacity)
{
this._vector = new MyValueType[initialCapacity];
this._upperBound = 0;
}
public MyValueType this[int index]
{
get { return this._vector[index]; }
set { this._vector[index] = value; }
}
public int Length { get { return this._upperBound; } }
public void Add(MyValueType newMember)
{
if (this._upperBound >= this._vector.Length - 1)
{
MyValueType newVector[] = new
MyValueType[this._vector.Length * 2];
for (int i = 0; i < this._vector.Length; i++)
{
newVector
= this._vector;
}
this._vector = newVector;
}
this._vector[this._upperBound] = newMember;
this._upperBound += 1;
}
}
Some notes:
1. Notice that the indexer does no bounds check. That is, it is
possible for client code to read/write "past the end of" the array"
(above the upperBounds limit) so long as it doesn't write outside the
physical capacity of the allocated array. This is for speed. Adding the
check will cost one additional comparison per reference / assignment,
and would look like this:
public MyValueType this[int index]
{
get
{
if (index >= this._upperBounds) { throw new
IndexOutOfBoundsException(...); }
return this._vector[index];
}
set
{
if (index >= this._upperBounds) { throw new
IndexOutOfBoundsException(...); }
this._vector[index] = value;
}
}
Even this omits the index < 0 check, as this will throw an exception on
the array access itself. Again, not the prettiest, but we're going for
speed, here.
2. The original indexer is sufficiently simple that the compiler /
JITter should in-line the calls, reducing indexed access to the
"vector" to a simple array access (one bounds comparison, one address
calculation, and one read or write) with no method call overhead. If it
turns out tha the JITter does not in-line calls, you could always make
_vector public and access it directly via myVector.Vector, which is
ugly but possible. You shouldn't have to do this, though, because the
compiler really should in-line such a simple indexer as the first one.
The second with the bounds check probably wouldn't be in-lined.
3. The class is _not_ thread-safe. In particular, the reallocation
operation is _not_ atomic and cannot be made atomic without slowing the
whole thing down tremendously by introducing locks around the indexer
getter and setter and the Add operation including reallocation. I
assume that you're not multi-threading.