views:

370

answers:

2

I want to emulate the following method:

ref class Something{
   void foo(array<double>^% data)
   {
      data = gcnew array<double>(10);
   }
};

Such that, the caller's array gets modified/created. Nevertheless, there are some constrains:

  1. foo cannot use ref/out/% nor any additional parameters.
  2. the actual parameter of foo has to be Object^.

Is it possible to do it with unmanaged pointers, IntPtr or a more obscure way?

+2  A: 

No. If the actual parameter of foo is a handle to an object, there is no way to change the calling stack's reference.

You need to use a tracking reference in order to be able to effect the handle passed into your method.


Edit in response to AZ's comment:

The reason this doesn't work, is that when you pass an Object handle, effectively, you're passing nothing but the location of the object in memory. At its core, this is basically just an integer holding the memory location.

When you assign a new object in your method, you're overriding that location (putting a new integer in place). That doesn't change the caller's original handle, since it's just a copy. This is the normal way of passing parameters.

By using a tracking reference, you're passing a reference to the location in memory. It's sort of like passing a pointer in C or C++. When you do that, you can change the location the reference points to (it's value) without changing the reference itself - only changing what it is referencing.

Reed Copsey
+1  A: 
  1. foo cannot use ref/out/% nor any additional parameters.
  2. the actual parameter of foo has to be Object^.

The caller could allocate the array and pass it in, and you could modify elements within the array - but this is just casting the array to Object and back again.

If you want to change the actual array - eg assign it to a new array of different length, without using ref (% in C++/CLI) or out ([Out] in C++/CLI), you can't do it.

Here's why:

The .NET virtual machine works by pushing and popping things onto a stack. The way you pass parameters to functions works like this behind the scenes:

  1. You Push the parameters (in this case, a reference to the array) onto the stack.
  2. You Call the function
  3. The VM sets up for the method call by reading these values off the stack in the specified order, and assigning them to local variables in your function.
  4. The code inside the function runs.

If the caller doesn't put valid data on the stack before invoking the function, it crashes. If the caller puts data in the wrong order - it crashes. If anything is funny with the stack at all - it crashes.

Additionally, methods cannot modify things that are already on the stack. (If they did, they would corrupt the stack - it crashes), they can only push and pop new things on and off the stack.

This means 2 things:

  1. In order to call the function, you have to put a reference to the array onto the stack. Therefore the caller has to allocate the array itself, or pass a null reference.

  2. You can't change the reference to the array, so the function can't provide a new array. As arrays are inherently non-resizeable, this means that you can't add new elements (you can however change existing elements)

Disclaimer: Eric lippert is probably going to come and explain why everything I've just written is wrong, but to the best of my knowledge and research, that's how it works

Orion Edwards
Nope, that seems reasonable. Your bit about "the VM reads the values off the stack and puts them into the locals" is slightly dodgy -- I assume by "locals" you mean the formal parameters. Since variables are storage locations, and the stack is a storage location, there's no need to read the values off the stack and put them somewhere else; the variables logically *are* the stack locations, no copying required. (Of course, an implementation *could* copy stuff off the stack into other locations, but why would it?)
Eric Lippert
I was joking about eric lippert coming along and correcting it, and then he did. StackOverflow is awesome :-)
Orion Edwards
BTW, by "puts them into the locals" I was trying to go for "makes them accessible via local variable names" without getting bogged down in low level details that might confuse the situation. Glad it's cleared up now.
Orion Edwards