FOREACH
Iterate sequentially through elements in a collection
WTSupported in traditional Synergy on Windows
|
WNSupported in Synergy .NET on Windows
|
USupported on UNIX
|
VSupported on OpenVMS
|
FOREACH loop_var IN collection [AS type] <iteration processing> . . .
Arguments
loop_var
A loop record or variable whose value is set to each element in sequence as FOREACH iterates through them.
collection
One of the following:
- A dynamic system array of rank 1 (for example, [#]@myclass)
- A real Synergy array of rank 1 (for example, [10]a5)
- The System.Collections.ArrayList class or a descendant
- The Synergex.SynergyDE.Collections.ArrayList class or a descendant
- The Synergex.SynergyDE.Select.Select class
- In traditional Synergy only, a class generated by the gennet/gennet40 utility that inherits from the DotNetObject class and can be converted to IEnumerable (a .NET interface that implements collection-like behavior)
- In Synergy .NET, anything that implements IEnumerable or IEnumerable<T>, with all elements of the collection being the same data type
type
(optional) The data type of the elements in the collection, which you’ll need to specify if the loop_var type doesn’t match the collection type.
Discussion
When the FOREACH statement is executed, it iterates sequentially through all elements in the collection, setting loop_var to each element in turn. If the collection is a dynamic array, the loop variable’s type must match or be an ancestor of the element type of that array; otherwise, a “Type mismatch” error (TYPMISMCH) will occur.
The loop variable is a copy of the processed element in collection, so it is like an assignment statement. This means that changes to members of loop_var are not reflected back in the original collection. As a result of the implied assignment, FOREACH is less efficient than a FOR loop.
You cannot add an element to the collection, remove an element from the collection, or set a collection item to another handle while the FOREACH statement is being executed. If any of these changes occur, the runtime will report an “Invalid operation: Collection was modified” error ($ERR_INVOPER).
A call to a label in a higher scope is not allowed in the iteration processing section of a FOREACH statement whose loop variable is an object.
If the collection is a Synergex.SynergyDE.Collections.ArrayList, a System.Collections.ArrayList, or a descendent of one of these classes and the items of the collection cannot be cast to the type of the loop variable, the runtime will report an “Incompatible classes” error ($ERR_INCPTCLS).
If the collection is a Synergex.SynergyDE.Select.Select class, the specified record is always retrieved.
In Synergy .NET, a collection that does not inherit from IEnumerable or IEnumerable<T> can still be used in a FOREACH loop if it has an extension method named GetEnumerator() that returns an IEnumerator. (See Examples below.) These extension methods can have different overloads and can also be defined on an interface. Only the overload with exactly one parameter will be chosen by the FOREACH loop. That first parameter must also have the correct type (the same as the class representing the collection). |
The loop variable must be the same type as the elements in the collection, or an “Invalid Cast” exception will occur. You can use the “AS type” syntax to accommodate cases in which the loop variable is different from the collection type and avoid generating an error. For example, if you had the following FOREACH statement
foreach mydecimalvar in arraylist
and you declare
ArrayList.Add(myi4var)
the decimal variable is not an Int and an error would occur. However, if your FOREACH statement were as follows, no error would occur:
foreach mydecimalvar in arraylist as @int
Similarly, if the collection contains elements that are type structfield and the loop variable is type a, your FOREACH statement might look like this:
foreach avar in arraylist as @structure_name
FOREACH also won’t work with a mixed-type array list, so make sure you either cast as you add items to the array list or use “AS type” in Synergy .NET. In traditional Synergy, FOREACH does work with mixed-type array lists, so you can use “AS type” to move code designed for traditional Synergy to Synergy .NET.
A non-object cannot be used as the loop variable if the IN variable is a collection, unless “AS type” is used in Synergy .NET to force a possible conversion. |
The enumerator created implicitly by the FOREACH statement is destroyed (disposed in .NET) at the end of the loop. In .NET, if the collection implements the dispose pattern and is a new instantiation, the temporary object created by the new instantiation has its Dispose method called automatically at the end of the loop.
A FOREACH loop can also iterate over an ASYNC collection. Place an AWAIT keyword before the FOREACH statement to iterate over the elements of an IAsyncEnumerable<T>, as shown in the third example below.
The example below iterates through the alist collection for every customer.
import System.Collections namespace ns1 class customer public name, string public method customer parm1, string proc name = parm1 endmethod endclass endnamespace main record alist ,@ArrayList c1 ,@customer proc alist = new ArrayList() alist.Add(new customer("joe")) alist.Add(new customer("fred")) alist.Add(new customer("fran")) open(2, o, "tt:") foreach c1 in alist begin writes(2, c1.name) end close(2) end
The example below uses a GetEnumerator extension method.
import System.Collections namespace ns public class Base endclass endnamespace namespace MyExtensions public static class MyExtensionsClass ;Put extension method on base class public extension static method GetEnumerator, @System.Collections.IEnumerator parm1, @Base record alist, @ArrayList proc alist = new ArrayList() alist.Add(1) alist.Add(2) mreturn alist.GetEnumerator() endmethod endclass endnamespace import MyExtensions main proc data ivar, int foreach ivar in new Base() Console.WriteLine(ivar) endmain
The following example shows a FOREACH loop that iterates over an ASYNC collection.
import System.Threading.Tasks import System.Collections.Generic namespace ns public class class1 public static async method Create, @IAsyncEnumerable<int> proc await Task.Delay(1) yield mreturn 100 yield mreturn 200 endmethod public static async method Consume, @Task proc data ivar, int await foreach ivar in Create() Console.WriteLine(ivar) endmethod endclass endnamespace