foreach

  • Thread starter Thread starter Michael Brown
  • Start date Start date
M

Michael Brown

Hi there. Can anybody comment on the efficiency of the following technique:

foreach (SomeItem Item in SomeNativeDotNet.Collection)
{
// Whatever
}

My concern is that the "Collection" property itself may be invoked on each
iteration rather than being called just once at the outset of "foreach". If
so then there's no way to know whether the call to retrieve this property is
a time-consuming operation (the property may not be stored as a simple
member variable). If so then things should be changed as follows:

Collection MyCollection = SomeNativeDotNet.Collection;
foreach (SomeItem Item in MyCollection)
{
// Whatever
}

Can anyone offer any insight. Thanks.
 
As I understand it, SomeNativeDotNet.Collection.GetEnumerator() will be
evaluated once, and then all subsequent access is onto the returned
enumerator (which will have its own direct access to the returned
collection); so no, this shouldn't call .Collection over and over...

Marc
 
Michael,

It's only executed once. You can open the assembly in ildasm to
verify.

Brian
 
As I understand it, SomeNativeDotNet.Collection.GetEnumerator() will be
evaluated once, and then all subsequent access is onto the returned
enumerator (which will have its own direct access to the returned
collection); so no, this shouldn't call .Collection over and over...

Thanks. I looked into the C# specification and this is apparently correct.
 
It's only executed once. You can open the assembly in ildasm to

Thanks for the feedback. You can't rely on the assembly however since it's
subject to change :) Only the language's official specification can be
trusted (and it apparently confirms what Marc Gravell said).
 
But does foreach create more objects than necessary. Say you have a collection like
Process[] RunningProcesses = Process.GetProcesses();
foreach(Process P in _RunningProcesses)
{ if (P.....some code }

The other option is
for(int i=0; i < RunningProcesses.Length;i++)
{if (RunningProcesses.....some code }

Does the first method create a new Process object in each loop and set it equal to the array object ?

Also, an unrelated question to this thread but for Process object, does Process.Close() release memory associated with the "process object" that is pointing to a running process or of the running process itself. The document is confusing. I tried a .Close on the entire array. It did not disrupt my system but just wanted to be sure.

TIA
 
Srini said:
Does the first method create a new Process object in each loop and set it equal to the array object ?

Of course not. Remeber C# is reference based. It does creates a new
reference, but that has no weight. To create a new process, you'd have
to say "new Process()" somewhere.
 
Srini said:
But does foreach create more objects than necessary.
Say you have a collection like

Process[] RunningProcesses = Process.GetProcesses();

foreach(Process P in _RunningProcesses)
{ if (P.....some code }

The other option is

for(int i=0; i < RunningProcesses.Length;i++)
{if (RunningProcesses.....some code }

Does the first method create a new Process object
in each loop and set it equal to the array object ?


No, neither of the loops creates any Process objects by themselves, they are
already created elsewhere, and retrieved with the "GetProcesses" method.

The first case just uses a single reference variable (Process P) which is
reused over and over again.

However, when you phrase it as "the other option is", I think some
clarification needs to be made, as the two loop types works in different
ways.

The second case uses the indexer of the array in order to iterate over the
objects.

The first case is behind the scenes something more like this construction:

IEnumerator rp = RunningProcesses.GetEnumerator();

while(rp.MoveNext())
{
Process P = (Process) rp.Current;
...some code...
}

....which usually involves a slightly bit more overhead in the use of the
IEnumerator implementation, than a simple iteration.
Also, an unrelated question to this thread but for Process object,
does Process.Close() release memory associated with the
"process object" that is pointing to a running process or
of the running process itself. The document is confusing.

"Frees all the resources that are associated with this component".

....which means that it frees all resources associated with the running
process.

I tried a .Close on the entire array.

I didn't know that the Array class even had a Close method... ;-)

....so I guess you mean that you called Close on each instance in the array.

// Bjorn A
 
Srini said:
But does foreach create more objects than necessary. Say you have a collection like
Process[] RunningProcesses = Process.GetProcesses();
foreach(Process P in _RunningProcesses)
{ if (P.....some code }

The other option is
for(int i=0; i < RunningProcesses.Length;i++)
{if (RunningProcesses.....some code }

Does the first method create a new Process object in each loop and set it equal to the array object ?


No. It just assigns a reference to the existing Process objects to the
P variable using the IEnumerator interface. The only object that is
actually created is the IEnumerator.
Also, an unrelated question to this thread but for Process object, does
Process.Close() release memory associated with the "process object"
that is pointing to a running process or of the running process itself.
The document is confusing. I tried a .Close on the entire array. It did not
disrupt my system but just wanted to be sure.

That is correct. You'd have to have to call Process.Kill or
Process.CloseMainWindow to actually shutdown the process.

Brian
 
On the first question it really depends what the enumerator does if you are dealing with an enumerator .. some enumerators will deal directly with the object in question. Some may choose to use a snapshot of the data (note that this can be extremely beneficial when dealing with multi-threading scenarios).

In this particular case you are being returned an array ... Your code will be identical ...

It is quite simple to see this if you use reflector to look at the generated code but I am including below the native code generated .. you can see it is nearly identical.

Process.Close is a bit confusing .. it deals with the internal resources that the process class is holding .. process.CloseWindow() used in the example will send a windows message telling the process to close. Process.Kill() will kill the process immediately.

Cheers,

Greg Young
MVP - C#
http://codebetter.com/blogs/gregyoung

Native code for simple example...
namespace ConsoleApplication7
{
class Program
{
public static void Main() {
Process[] Processes = Process.GetProcesses();
00000000 push edi
00000001 push esi
00000002 push ebx
00000003 call 79A13448
00000008 mov esi,eax
foreach (Process p in Processes)
0000000a xor ebx,ebx
0000000c cmp dword ptr [esi+4],0
00000010 jle 00000053
00000012 cmp ebx,dword ptr [esi+4]
00000015 jae 0000009C
0000001b mov ecx,dword ptr [esi+ebx*4+0Ch]
{
Console.WriteLine(p.ProcessName);
0000001f cmp dword ptr [ecx],ecx
00000021 call 79C26558
00000026 mov edi,eax
00000028 cmp dword ptr ds:[02271084h],0
0000002f jne 0000003B
00000031 mov ecx,1
00000036 call 786FC654
0000003b mov ecx,dword ptr ds:[02271084h]
00000041 mov edx,edi
00000043 mov eax,dword ptr [ecx]
00000045 call dword ptr [eax+000000D8h]
0000004b add ebx,1
foreach (Process p in Processes)
0000004e cmp dword ptr [esi+4],ebx
00000051 jg 00000012
}
for (int i = 0; i < Processes.Length; i++)
00000053 xor ebx,ebx
00000055 cmp dword ptr [esi+4],0
00000059 jle 00000098
{
Process p = Processes;
0000005b cmp ebx,dword ptr [esi+4]
0000005e jae 0000009C
00000060 mov ecx,dword ptr [esi+ebx*4+0Ch]
Console.WriteLine(p.ProcessName);
00000064 cmp dword ptr [ecx],ecx
00000066 call 79C26558
0000006b mov edi,eax
0000006d cmp dword ptr ds:[02271084h],0
00000074 jne 00000080
00000076 mov ecx,1
0000007b call 786FC654
00000080 mov ecx,dword ptr ds:[02271084h]
00000086 mov edx,edi
00000088 mov eax,dword ptr [ecx]
0000008a call dword ptr [eax+000000D8h]
for (int i = 0; i < Processes.Length; i++)
00000090 add ebx,1
00000093 cmp dword ptr [esi+4],ebx
00000096 jg 0000005B
00000098 pop ebx
}
}
00000099 pop esi
0000009a pop edi
0000009b ret
0000009c call 79443529
000000a1 int 3
But does foreach create more objects than necessary. Say you have a collection like
Process[] RunningProcesses = Process.GetProcesses();
foreach(Process P in _RunningProcesses)
{ if (P.....some code }

The other option is
for(int i=0; i < RunningProcesses.Length;i++)
{if (RunningProcesses.....some code }

Does the first method create a new Process object in each loop and set it equal to the array object ?

Also, an unrelated question to this thread but for Process object, does Process.Close() release memory associated with the "process object" that is pointing to a running process or of the running process itself. The document is confusing. I tried a .Close on the entire array. It did not disrupt my system but just wanted to be sure.

TIA
 
foreach is special cased for arrays .. see my previous post.

Cheers,

Greg Young
MVP - C#
http://codebetter.com/blogs/gregyoung
Bjorn Abelli said:
Srini said:
But does foreach create more objects than necessary.
Say you have a collection like

Process[] RunningProcesses = Process.GetProcesses();

foreach(Process P in _RunningProcesses)
{ if (P.....some code }

The other option is

for(int i=0; i < RunningProcesses.Length;i++)
{if (RunningProcesses.....some code }

Does the first method create a new Process object
in each loop and set it equal to the array object ?


No, neither of the loops creates any Process objects by themselves, they
are already created elsewhere, and retrieved with the "GetProcesses"
method.

The first case just uses a single reference variable (Process P) which is
reused over and over again.

However, when you phrase it as "the other option is", I think some
clarification needs to be made, as the two loop types works in different
ways.

The second case uses the indexer of the array in order to iterate over the
objects.

The first case is behind the scenes something more like this construction:

IEnumerator rp = RunningProcesses.GetEnumerator();

while(rp.MoveNext())
{
Process P = (Process) rp.Current;
...some code...
}

...which usually involves a slightly bit more overhead in the use of the
IEnumerator implementation, than a simple iteration.
Also, an unrelated question to this thread but for Process object,
does Process.Close() release memory associated with the
"process object" that is pointing to a running process or
of the running process itself. The document is confusing.

"Frees all the resources that are associated with this component".

...which means that it frees all resources associated with the running
process.

I tried a .Close on the entire array.

I didn't know that the Array class even had a Close method... ;-)

...so I guess you mean that you called Close on each instance in the
array.

// Bjorn A
 
Thanks to you all, it clears up lot of things for me on the foreach.


Here is what is says in the help example for CLose method on Process object.

Process myProcess;
myProcess = Process.Start("Notepad.exe");
// Close process by sending a close message to its main window.
myProcess.CloseMainWindow();
// Free resources associated with process.
myProcess.Close();

If I call the Close without calling CloseMainWindow or Kill, it does NOT throw exception nor does it do any damage(like closing applications). Thats why it was unclear to me if it frees resource of the myProcess object or that of Notepad.

In my case, I am not closing any process. I want to implement a Process Handler which implements IDisposable which instantiates the array in the constructor and does a close on each Process object of the array in the Dispose. The user could call publicly exposed Kill or CloseMainWindow functions as needed. May be I do not have to do this since Process is a managed object. But I wanted to clean up the objects as soon as possible as this could be used by windows service type applications.

Thanks
On the first question it really depends what the enumerator does if you are dealing with an enumerator .. some enumerators will deal directly with the object in question. Some may choose to use a snapshot of the data (note that this can be extremely beneficial when dealing with multi-threading scenarios).

In this particular case you are being returned an array ... Your code will be identical ...

It is quite simple to see this if you use reflector to look at the generated code but I am including below the native code generated .. you can see it is nearly identical.

Process.Close is a bit confusing .. it deals with the internal resources that the process class is holding .. process.CloseWindow() used in the example will send a windows message telling the process to close. Process.Kill() will kill the process immediately.

Cheers,

Greg Young
MVP - C#
http://codebetter.com/blogs/gregyoung

Native code for simple example...
namespace ConsoleApplication7
{
class Program
{
public static void Main() {
Process[] Processes = Process.GetProcesses();
00000000 push edi
00000001 push esi
00000002 push ebx
00000003 call 79A13448
00000008 mov esi,eax
foreach (Process p in Processes)
0000000a xor ebx,ebx
0000000c cmp dword ptr [esi+4],0
00000010 jle 00000053
00000012 cmp ebx,dword ptr [esi+4]
00000015 jae 0000009C
0000001b mov ecx,dword ptr [esi+ebx*4+0Ch]
{
Console.WriteLine(p.ProcessName);
0000001f cmp dword ptr [ecx],ecx
00000021 call 79C26558
00000026 mov edi,eax
00000028 cmp dword ptr ds:[02271084h],0
0000002f jne 0000003B
00000031 mov ecx,1
00000036 call 786FC654
0000003b mov ecx,dword ptr ds:[02271084h]
00000041 mov edx,edi
00000043 mov eax,dword ptr [ecx]
00000045 call dword ptr [eax+000000D8h]
0000004b add ebx,1
foreach (Process p in Processes)
0000004e cmp dword ptr [esi+4],ebx
00000051 jg 00000012
}
for (int i = 0; i < Processes.Length; i++)
00000053 xor ebx,ebx
00000055 cmp dword ptr [esi+4],0
00000059 jle 00000098
{
Process p = Processes;
0000005b cmp ebx,dword ptr [esi+4]
0000005e jae 0000009C
00000060 mov ecx,dword ptr [esi+ebx*4+0Ch]
Console.WriteLine(p.ProcessName);
00000064 cmp dword ptr [ecx],ecx
00000066 call 79C26558
0000006b mov edi,eax
0000006d cmp dword ptr ds:[02271084h],0
00000074 jne 00000080
00000076 mov ecx,1
0000007b call 786FC654
00000080 mov ecx,dword ptr ds:[02271084h]
00000086 mov edx,edi
00000088 mov eax,dword ptr [ecx]
0000008a call dword ptr [eax+000000D8h]
for (int i = 0; i < Processes.Length; i++)
00000090 add ebx,1
00000093 cmp dword ptr [esi+4],ebx
00000096 jg 0000005B
00000098 pop ebx
}
}
00000099 pop esi
0000009a pop edi
0000009b ret
0000009c call 79443529
000000a1 int 3
But does foreach create more objects than necessary. Say you have a collection like
Process[] RunningProcesses = Process.GetProcesses();
foreach(Process P in _RunningProcesses)
{ if (P.....some code }

The other option is
for(int i=0; i < RunningProcesses.Length;i++)
{if (RunningProcesses.....some code }

Does the first method create a new Process object in each loop and set it equal to the array object ?

Also, an unrelated question to this thread but for Process object, does Process.Close() release memory associated with the "process object" that is pointing to a running process or of the running process itself. The document is confusing. I tried a .Close on the entire array. It did not disrupt my system but just wanted to be sure.

TIA
 
Back
Top