|
It is possible that I am the only person in the world who didn't already know this , but there is one very important difference between the behavior of a For Each loop that iterates through an array (or an array of Structures, which was the context that has just bitten me!) and the corresponding For Next loop. When you use For Each, the item that is passed into the loop is effectively passed by Value. Any assignment that you make to it within the loop is lost as soon as you leave it. If you put exactly the same code within an equivalent For Next loop, the assignments "stick".
The following very short Console Application demonstrates this:
Module Module1
Dim intArray(10) As Integer
Sub Main()
Dim intI As Integer
For intI = 0 To 10 ' fill the array
intArray(intI) = intI
Next
For Each item In intArray ' show that the data has been stored
Console.Write(item.ToString & ", ")
Next
Console.Write(vbCrLf)
intI = 10
For Each item In intArray ' fill it again in reverse order
item = intI
Console.Write(item.ToString & ", ") ' show that contents of the "local" copy of the array element has been changed
intI -= 1
Next
Console.Write(vbCrLf)
For Each item In intArray
Console.Write(item.ToString & ", ") ' but the contents of the actual array have not!
Next
Console.Write(vbCrLf)
Console.ReadLine()
End Sub
End Module
I was (eventually) able to find documentation of this behavior in the VB .Net docs (ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/dv_vbalr/html/ebce3120-95c3-42b1-b70b-fa7da40c75e2.htm), but I could not find any explicit mention of it in any of the books I have or in any other on-line resources, even though all of the latter do only show For Each being used in ways that work. I am hoping that this note may help others to avoid the head-scratching that I went through before if figured out why my code was not working.
modified on Monday, September 13, 2010 11:27 AM
|
|
|
|
|
I'm not sure I ever used a For Each in VB.NET; however the same is true in C#, and there the compiler would not even let you modify the variable, so I was surprised to see VB.NET lets you do item = intI at all.
BTW: I trust the snippet is just an example of the phenomenon. If you want to turn an array upside down, you could as well use Array.Reverse()
|
|
|
|
|
Yes, it was just an intentionally trivial example. I wouldn't have minded (much ), and I certainly wouldn't have wasted the time I ended up spending on the issue, if the compiler had flagged the assignment as an error.
I don't see why this restriction should be imposed (presumably) by the compiler writers. For an array of substantial structures, it would seem to add complexity and cycles (copying everything instead of passing pointers) to no good purpose.
|
|
|
|
|
There is no willful copying. If it were an array of Forms, you'd get a reference to each Form in turn; as it is an array of ints, you get each int in turn (and not a pointer to an array element). It would be exactly the same if the body of your foreach loop were a separate method, value types get passed by value, reference types get also passed by value, i.e. you get their reference (not a pointer to their reference). That works fine, except for large value types (but then you are always given the advice not to come up with big structs).
I'm not absolutely sure why the languages were designed like that; I realize the collection (the array in your example) could be virtual and could be read-only; all foreach requires is something that is enumarable, so arays, lists, an anything implementing IEnumerable should do. Elements of some of those would not be modifiable by sending a new value through some pointer.
|
|
|
|
|
If I were calling a Sub, I would find it natural (and well-documented!) that I was getting a Value rather than a Reference in the Sub (and I could explicitly change that, if I wanted to). I think that is the in-line nature of and the almost exact parallel between the For Each and For Next syntax and operation, except for this one important detail, that makes the former's behavior feel wrong.
For a simple, one-dimensional, array of any simple value or object type, you are right - the "pass by value" approach will result in no increase in code size or complexity - but even for an array of relatively small structs, a lot of copying will be necessary to (in effect) pass each struct item by value.
|
|
|
|
|
Peter R. Fletcher wrote: a lot of copying will be necessary to (in effect) pass each struct item by value.
And that makes me think it is the reason why C# does not accept any changes to it: they probably don't copy but use the real stuff, which is fine if they disallow any code to change it.
|
|
|
|
|
Actually, they do make a copy, even in the optimized code.
In fact, two copies are made, at least in the code I tested.
See here:
shl rcx,4
mov eax,dword ptr [rbx+rcx+10h]
mov dword ptr [rsp+20h],eax
mov eax,dword ptr [rbx+rcx+14h]
mov dword ptr [rsp+24h],eax
mov eax,dword ptr [rbx+rcx+18h]
mov dword ptr [rsp+28h],eax
mov eax,dword ptr [rbx+rcx+1Ch]
mov dword ptr [rsp+2Ch],eax
lea rcx,[rsp+20h]
mov rax,qword ptr [rcx]
mov qword ptr [rsp+40h],rax
mov rax,qword ptr [rcx+8]
mov qword ptr [rsp+48h],rax
lea rax,[rsp+40h]
movss xmm5,dword ptr [rax]
The C# code:
static void Normalize(Float3[] array)
{
Float3 x = new Float3();
foreach (Float3 f in array)
{
x = f.Normalize();
}
if (array.Length != 0)
throw new Exception(x.ToString());
}
edit: made a slight mistake here, whatever.
If you change "the foreach value" (indirectly, of course), the second copy is affected but not the first. The first copy is never used again.
For primitive types, the code is actually sane.
modified on Monday, September 13, 2010 5:39 PM
|
|
|
|
|
You're sure that is a release situation? doesn't look very good then.
|
|
|
|
|
Release without debugger attached, then attach it later, I know
And yes, it's quite horrible..
edit: for primitive types they just read the value from the array and use it
|
|
|
|
|
harold aptroot wrote: for primitive types they just read the value from the array and use it
OK, one more reason to tip the class/struct balance in favor of struct then.
BTW: great info!
|
|
|
|
|
Thanks
By the way, they don't disallow all code to change "the foreach value". For example, if you have an array of Rectangle s you can still call Inflate on them. Code like that has the effect of changing the second copy, and that's the same as changing "the foreach value".
This snippet shows that behaviour:
Rectangle[] aBunchOfRectangles = new Rectangle[10];
foreach (Rectangle rect in aBunchOfRectangles)
{
Console.WriteLine(rect);
rect.Inflate(1, 1);
Console.WriteLine(rect);
}
That would explain why the struct is copied from the array to the stack once (though it is also done when the method doesn't change the instance), the other copy is still a mystery to me..
|
|
|
|
|
harold aptroot wrote: if you have an array of Rectangles you can still call Inflate on them
Yeah, Rectangle is a mutable struct! I assume they didn't make it immutable for some backward compatibility reason. I had never considered the implications of this (apart from the normal) until seeing your previous post - interesting
|
|
|
|
|
Hi
I'm a newbie programming .NET and need some help.
I have a windows form project in which I have instantiated a trackbar_scroll component (a horizontal slider). I'd like it to stay in the lower right corner of the window when resizing but I can't make it
What should I code in this method:
private void trackBar1_Scroll(object sender, EventArgs e)
{
}
Can someone please tell me how to do this?
Cheers
Tom
|
|
|
|
|
|
Yeah that did it!
Thanks...hmmmm, I have a looooong way to go!
Thanks
Tom
|
|
|
|
|
hi friends,
i want to insert value in a particular column of a row(datagridview) from a text box.
please help!
|
|
|
|
|
Sorry, but your question is not clear enough. Do you mean put the text from text box into grid?
In that case you can just access that particular column of the datagridview and then set it.
The funniest thing about this particular signature is that by the time you realise it doesn't say anything it's too late to stop reading it.
My latest tip/trick
Visit the Hindi forum here.
|
|
|
|
|
sorry for late response
Basically i have a button and i try to insert value in datagridview on button click.Please help me
|
|
|
|
|
I was curious, I work for a PC repair shop, and we use alot of applications to get certain jobs done, but I also have a certain amount of applications that I use for OS Support, is there any way to deploy applcations we use using the Deploy and Package wizard to install it on out customers computers, for example, adding Malwarebytes dotnet4 and java offline installer in a deployment package?
|
|
|
|
|
Have a look here[^]. This link describes how to use custom file types while creating a setup.
The funniest thing about this particular signature is that by the time you realise it doesn't say anything it's too late to stop reading it.
My latest tip/trick
Visit the Hindi forum here.
|
|
|
|
|
http://ninite.com/[^]
Regards,
Thomas Stockwell
Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the Universe trying to produce bigger and better idiots. So far, the Universe is winning.
Visit my Blog
|
|
|
|
|
Is that what your customers want?
|
|
|
|
|
Hi,
I am trying to make a kind of desktop widgets using C#. So far, I've managed to create a window that gets added to the WorkerW window containing SHELLDLL_DefView. This way, my main form is inserted inside the desktop window over the wallpaper. This is exactly what I want except that I have a little problem. It seems like there is an opacity of 50% and a black is rendered as transparent. I don't know where this comes from and I don't know if I can change it. Does anybody knows what might be causing this and how can I fix it?
The code I used to do this is pretty simple. I find the handle to the WorkerW window that contains the SHELLDLL_DefView window as a child and then I call those functions to insert my own form inside as a child :
SetParent(this.Handle, hWndParent);
SetWindowLong(this.Handle, -16, new IntPtr(GetWindowLong(this.Handle, -16) | 0x40000000));
Here's how it looks when the form is in the desktop
Thank you
|
|
|
|
|
If you are trying to create a desktop widget then do some research with the Sidebar Gadget projects. The way you are doing it could be problematic.
Regards,
Thomas Stockwell
Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the Universe trying to produce bigger and better idiots. So far, the Universe is winning.
Visit my Blog
|
|
|
|
|
After creating a VB.NET app in .NET Framework 4 I noticed that when I try using the application on several levels of windows whether it be XP/Vista or 7 it keeps throwing an error bout installing the suggested framework on the computer. How do I compile it so it is compatible with several OS's without the user having to install the .NET Framework in order for it to run?
|
|
|
|
|