1
Sorry for the late response. I had some time off and got pretty busy!
I didn't think that this was using dynamic arrays, so most of your argument wouldn't apply. I think the code that I *posted* would be using a dynamic array, even though it's initialized, but you can't do that in a struct (my posted code was erroneous). The code that I actually tested did not use a dynamic array.
Okay... I'll agree that you cannot have a dynamic array on the stack, because it couldn't be resized without rebuilding part of the stack, which screws up any stack pointers elsewhere in code. That said, the array size of 4 bytes could be anything, not just a reference. It's not clear what you mean by 12 when it contains the 3 floats.. WHAT has a size of 12 - the struct, or the array? I'm assuming you mean the array. While a reference size should NEVER change (say from 4 to 12), I suspect that if you're right, the 4 byte value is a NULL, which cannot be dereferenced, and the 12 bytes are the size of the dereferenced value.
Further, you're correct that the allocation of the array could be elsewhere than the constructor, but a compiler can be made to know the difference between a literal or constant array size in an array constructor, and a variable for the array size. This is the case with most compilers I've used. However, it is my experience that they are not distinguished under normal circumstances in c#, so I doubt they are distinguished in a struct, either. I don't have VS on this machine and can't try it right now.
Beyond being argumentative, though, there is something to be said that the size CHANGED (if I'm correctly interpreting what you're saying), regardless of whether or not it's a dereference issue or an actual size change, both would mean that it must be on the heap, so it also doesn't matter if it's dynamic or not. It' treating it as if it were. Given that... your goal still remains. I still profess that the point to using a struct is that it's passed by value instead of by reference, which is a purpose undefeated by a dynamic array being created on the heap. You're faced only with a slight performance decrease.
But you're the boss :) The only other way that I can think of is to use stackalloc and return the address of a fixed-length array in an unsafe code block. Maybe a pointer could be cast to an array, or maybe you can use this in tandem with a union the way you originally intended. This is probably not a good way to do this, but it's all I've got. I've never used unsafe code in C#, so I'm not certain that it would work.
Sorry I couldn't be more helpful... Good luck!

1
If you do Marshal.SizeOf on the struct, you will see that the size with the array is 4 (which is what it should be if it only containts a reference to an array), and its size is 12 when it contains the 3 floats. Just think about it, when you do data = new float[3], the 3 could theoretically be any number, infact the struct could even take a variable size that determines its internal arrays size, and having variable sized structs would definitly not work (the whole theory of value types and passing by value would be un-applyable).
Dynamic arrays can NEVER be on the stack. The assembly code you posted just shows that the same process happens when you construct a new Vector, which is true. Its only later on in the constructor where the dynamic allocation the happens to the array, which will be different.
So basically, the struct IS created on the STACK like you said; but the struct will only contain a single 4byte integer which is the reference to an array which is created on the HEAP.
1
Are you sure it will be created on the heap? How do you know that?
I don't see how that could happen when a struct must be created on the stack, so must its members, right? To prove it, I wrote code that uses 3 individual float values instead of an array, and compared the disassembly of the construction of both. They are both created no the stack and are exactly the same except for the 7 byte offset caused by removing the asarray property from the struct. You don't have to know assembly to see that it makes absolutely no difference that these 3 values are represented internally in the same way when usnig an array of floats instead of individual floats.
//struct vector() {} with an array.
vector mysruct = new vector( 5, 6, 7 );
00000182 push 40A00000h
00000187 push 40C00000h
0000018c push 40E00000h
00000191 lea ecx,[ebp-34h]
00000194 call dword ptr ds:[003E6B1Ch]
//struct vector() {} without array.
vector mysruct = new vector( 5, 6, 7 );
00000188 push 40A00000h
0000018d push 40C00000h
00000192 push 40E00000h
00000197 lea ecx,[ebp-3Ch]
0000019a call dword ptr ds:[003E6B50h]
If you're not familiar with assembly, you can compare the very same experiment with the very same struct when it is defined as a CLASS (otherwise, the code is identical) and the disassembly thereof. This is what it looks like when it uses the heap.
//class vector() {} with an array.
vector mysruct = new vector( 5, 6, 7 );
00000189 mov ecx,3E6AC4h
0000018e call F9211B48
00000193 mov esi,eax
00000195 push 40A00000h
0000019a push 40C00000h
0000019f push 40E00000h
000001a4 mov ecx,esi
000001a6 call dword ptr ds:[003E6AFCh]
000001ac mov dword ptr [ebp-34h],esi
//class vector() {} without array.
// vector mysruct = new vector( 5, 6, 7 );
00000189 mov ecx,3E6AB0h
0000018e call F9211B48
00000193 mov esi,eax
00000195 push 40A00000h
0000019a push 40C00000h
0000019f push 40E00000h
000001a4 mov ecx,esi
000001a6 call dword ptr ds:[003E6AE8h]
000001ac mov dword ptr [ebp-34h],esi

1
Because I need the x, y and z to be value types stored within the struct. In your example, when the array is created, it will allocate a dynamic array on the heap; in essence removing the point of making the class a struct.
1
You're saying that your class has 3 floats. You need array access to them. You don't want to build an array each time it's asked for. So why not create the array ONCE and just modify it as needed, then return a reference to it when asked?
Consider the following code. This would allow you to access the values individually, or as an array. Since the values are stored internally as an array, you only construct the array object once, and you just use the properties to set the individual values, inside or outside the object. As an object, a class instance could be passed around just as easily as data, bur requires a reference. Structs do not require references because they are value-oriented instead of reference-oriented, but they can still contain the code necessary to do any conversions and manage all their data.
public struct vector{
private float[] valuearray = new float[] {0, 0, 0};
public vector(float x, float y, float z){
float[0] = x;
float[1] = y;
float[2] = z;
}
public float[] AsArray {
get{
return values;
}
set{
if (value.Length == 3)
values=value;
else
throw new Exception("Array has an incorrect number of elements.");
}
}
public float x {
get {
return values[0];
}
set {
values[0] = value;
}
}
public float y {
get {
return values[1];
}
set {
values[1] = value;
}
}
public float z {
get {
return values[2];
}
set {
values[2] = value;
}
}
}
