3

I have a custom Vector class that can be initialized several ways. It has the following constructor to create a vector of a given length with all zeros.

public Vector(int length) { ... }

or it can use an object initializer by implementing IEnumerable and an Add(..) method like this to fill out a given vector

var v1 = new Vector{ 1, 2, 5 };

The problem lies in the fact that you can write the following code

var v1 = new Vector(3){ 1, 2, 5 };

this looks innocent enough, but it ends up creating a vector of length 6 where the first three values are 0 and then the next 3 are 1,2,5. Ideally this would return just a vector of length 3 with values of 1,2,5. I have gone through and stepped through the object intialization of this and there does not seem to be any indication that the object is being created. It appears as though the constructor runs, and then 3 Add() methods are being called. How can I tell that it is an object initializer calling the Add() method and protect the method from creating additional values?

p.s. As is evident in the comment section the API for this class isn't great as it stands in the question. Still a work in progress, but I appreciate the commentary as it helped me flush out my ideas a little further. I'm leaving the question as is so the comments make sense.

jruizaranguren
  • 12,679
  • 7
  • 55
  • 73
PlTaylor
  • 7,345
  • 11
  • 52
  • 94
  • 3
    Why does a `length` argument add elements to the underlying array? That can be very confusing to the caller. – Yuval Itzchakov Oct 28 '15 at 13:59
  • 2
    I would suggest your `Vector(3)` to me would indicate a `capacity` not an element. So your implementation seems off. – GreatAndPowerfulOz Oct 28 '15 at 14:01
  • 2
    You can't tell, the compiler converts the initialiser into a sequence of `Add` calls. The real problem is that your API is confusing. – Lee Oct 28 '15 at 14:01
  • You can add a break point and then look at the "trace stack" which you can add from the view menu. – jdweng Oct 28 '15 at 14:01
  • Vector(3) creates a class that has a capacity for 3 doubles. – PlTaylor Oct 28 '15 at 14:02
  • Why are you creating a custom collection in the first place? Just use `List` – Servy Oct 28 '15 at 14:02
  • @Servy I am creating a custom class so I can implement Vector math. Doing List + List doesn't make a bunch of sense. – PlTaylor Oct 28 '15 at 14:03
  • @PlTaylor That's not what you wrote in the question. In the question, you say `Vector(3)` sets the length to 3. The length and the capacity are two different things. I too would expect `Vector(3)` to only set the capacity to 3. Which would mean the length would be zero, but the `Add` methods won't require allocations. –  Oct 28 '15 at 14:03
  • @PlTaylor, OK, so you need a `capacity` and a `length`. Your `Vector(int length)` should really be `Vector(int capacity)` and only change the `length` when you add items. – GreatAndPowerfulOz Oct 28 '15 at 14:04
  • @hvd I have always seen capacity and length as interchangable terms. Can you point to definitions where they are different so I can better see your point? – PlTaylor Oct 28 '15 at 14:04
  • 1
    @PlTaylor Then just have your `Vector` be a wrapper around a `List` and emulate its semantics for the operations you expose. – Servy Oct 28 '15 at 14:06
  • 1
    @PlTaylor .NET's own `List` uses them differently, uses them the same way the commenters here use them. –  Oct 28 '15 at 14:06
  • @PlTaylor, see c#'s `ArrayList` class for an example. It has both `Capacity` and `Length` with separate meanings. – GreatAndPowerfulOz Oct 28 '15 at 14:06
  • @Gread.And.Powerful.Oz `ArrayList` has been obsolete for 10 years. You should be looking at `List`. – Servy Oct 28 '15 at 14:08
  • @Servy ::rollseyes:: It answered the question, therefore it's relevant. – GreatAndPowerfulOz Oct 28 '15 at 14:12
  • The goal was to have a Vector that had an Immutable length, the only reason I implemented an Add() was to be able to initialize vectors with values easily. Trying to feel out a reasonable API for this as it doesn't have one yet. – PlTaylor Oct 28 '15 at 14:13
  • @PlTaylor If you want it to be immutable, then you shouldn't add public methods to mutate it. – Servy Oct 28 '15 at 14:18
  • @Servy just trying to find a balancing point of ease of use and ideal implementation. – PlTaylor Oct 28 '15 at 14:20

2 Answers2

2

This looks like a design problem. Instead of going around and trying to tell that the compiler is going to invoke your Add method, I would change the Vector constructor to have an overload receiving the actual values, or an array of them:

public Vector(double x, double y, double z)

Or

public Vector(params double[] values)

Where the default constructor would hold the default values for the elements without actually adding anything to the underlying array.

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • The vector will not always be of length 3, but your second point is really valid. – PlTaylor Oct 28 '15 at 14:07
  • 1
    That point is well received. Playing around with implementations now. – PlTaylor Oct 28 '15 at 14:09
  • For the record the best balance I have found so far is to implement the param constructor and get rid of the object intialization all together. But it is still a work in progress. – PlTaylor Oct 28 '15 at 14:22
1

How can I tell that it is an object intializer calling the Add() method and protect the method from creating additional values?

You don't. The code is functionally identical to:

var v1 = new Vector(3);
v1.Add(1);
v1.Add(2);
v1.Add(3);

As far as your class is concerned, the two usages are indistinguishable.

You can ensure that anyone who writes that has it function however you think it should, and the collection initializer code will do the same thing.

Servy
  • 202,030
  • 26
  • 332
  • 449