/// <summary> /// Performs replacements on deeply-copied array. Performs deep copies of replace values. /// </summary> internal static void ArrayReplaceImpl(PhpArray array, PhpArray replaceWith, bool recursive) { if (array != null && replaceWith != null) { var iterator = replaceWith.GetFastEnumerator(); while (iterator.MoveNext()) { PhpValue tmp; var entry = iterator.Current; if (recursive && entry.Value.IsArray && (tmp = array[entry.Key]).IsArray) { ArrayReplaceImpl(tmp.Array, entry.Value.Array, true); } else { array[entry.Key] = entry.Value.DeepCopy(); } } } }
/// <summary> /// Returns array which elements are taken from a specified one in reversed order. /// </summary> /// <param name="array">The array to be reversed.</param> /// <param name="preserveKeys">Whether keys should be left untouched. /// If set to <b>false</b> then integer keys are reindexed starting from zero.</param> /// <returns>The array <paramref name="array"/> with items in reversed order.</returns> public static PhpArray array_reverse(PhpArray array, bool preserveKeys = false) { if (array == null) { //PhpException.ReferenceNull("array"); //return null; throw new ArgumentNullException(); } PhpArray result = new PhpArray(); var e = array.GetFastEnumerator(); if (preserveKeys) { // changes only the order of elements: while (e.MoveNext()) { result.Prepend(e.CurrentKey, e.CurrentValue); } } else { // changes the order of elements and reindexes integer keys: int i = array.IntegerCount; while (e.MoveNext()) { var key = e.CurrentKey; result.Prepend(key.IsString ? key : new IntStringKey(--i), e.CurrentValue); } } // if called by PHP languge then all items in the result should be inplace deeply copied: //result.InplaceCopyOnReturn = true; return result; }
/// <summary> /// Retuns the specified array. /// see http://php.net/manual/en/function.array-filter.php /// </summary> /// <remarks>The caller argument is here just because of the second Filter() method. Phalanger shares the function properties over the overloads.</remarks> public static PhpArray array_filter(PhpArray array) { if (array == null) { //PhpException.ArgumentNull("array"); //return null; throw new ArgumentNullException(nameof(array)); } var result = new PhpArray(); var enumerator = array.GetFastEnumerator(); while (enumerator.MoveNext()) { var entry = enumerator.Current; if (entry.Value.ToBoolean()) { result.Add(entry); } } return result; }
/// <summary> /// Filters an array using a specified callback. /// </summary> /// <param name="ctx">Current runtime context.</param> /// <param name="array">The array to be filtered.</param> /// <param name="callback"> /// The callback called on each value in the <paramref name="array"/>. /// If the callback returns value convertible to <B>true</B> the value is copied to the resulting array. /// Otherwise, it is ignored. /// </param> /// <returns>An array of unfiltered items.</returns> //[return: PhpDeepCopy] public static PhpArray array_filter(Context ctx /*, caller*/, PhpArray array, IPhpCallable callback) { if (array == null) { //PhpException.ArgumentNull("array"); //return null; throw new ArgumentNullException(nameof(array)); } if (callback == null) { //PhpException.ArgumentNull("callback"); //return null; throw new ArgumentNullException(nameof(callback)); } var result = new PhpArray(); var args = new PhpValue[1]; var iterator = array.GetFastEnumerator(); while (iterator.MoveNext()) { var entry = iterator.Current; // no deep copying needed because it is done so in callback: args[0] = entry.Value; // adds entry to the resulting array if callback returns true: if (callback.Invoke(ctx, args).ToBoolean()) { result.Add(entry); } } // values should be inplace deeply copied: //result.InplaceCopyOnReturn = true; return result; }
static PhpValue preg_replace(Context ctx, string pattern, string replacement, PhpCallable callback, PhpValue subject, int limit, ref long count) { var regex = new PerlRegex.Regex(pattern); // TODO: count // TODO: callback var subject_array = subject.AsArray(); if (subject_array == null) { return PhpValue.Create(regex.Replace(subject.ToStringOrThrow(ctx), replacement, limit)); } else { var arr = new PhpArray(subject_array, false); var enumerator = arr.GetFastEnumerator(); while (enumerator.MoveNext()) { var newvalue = regex.Replace(enumerator.CurrentValue.ToStringOrThrow(ctx), replacement, limit); enumerator.CurrentValue = PhpValue.Create(newvalue); } return PhpValue.Create(arr); } }
/// <summary> /// Adds items of "array" to "result" merging those whose string keys are the same. /// </summary> private static bool MergeRecursiveInternal(PhpArray/*!*/ result, PhpArray/*!*/ array, bool deepCopy) { var visited = new HashSet<object>(); // marks arrays that are being visited using (var iterator = array.GetFastEnumerator()) while (iterator.MoveNext()) { var entry = iterator.Current; if (entry.Key.IsString) { if (result.ContainsKey(entry.Key)) { // the result array already contains the item => merging take place var xv = result[entry.Key]; var y = entry.Value.GetValue(); // source item: PhpValue x = xv.GetValue(); // if x is not a reference then we can reuse the ax array for the result // since it has been deeply copied when added to the resulting array: PhpArray item_result = (deepCopy && x.IsArray && !xv.IsAlias) ? x.Array : new PhpArray(); if (x.IsArray && y.IsArray) { var ax = x.Array; var ay = y.Array; if (ax != item_result) ax.AddTo(item_result, deepCopy); if (visited.Add(ax) == false && visited.Add(ay) == false) return false; // merges ay to the item result (may lead to stack overflow, // but only with both arrays recursively referencing themselves - who cares?): bool finite = MergeRecursiveInternal(item_result, ay, deepCopy); visited.Remove(ax); visited.Remove(ay); if (!finite) return false; } else { if (x.IsArray) { if (x.Array != item_result) x.Array.AddTo(item_result, deepCopy); } else { /*if (x != null)*/ item_result.Add(deepCopy ? x.DeepCopy() : x); } if (y.IsArray) y.Array.AddTo(item_result, deepCopy); else /*if (y != null)*/ item_result.Add(deepCopy ? y.DeepCopy() : y); } result[entry.Key] = PhpValue.Create(item_result); } else { // PHP does no dereferencing when items are not merged: result.Add(entry.Key, (deepCopy) ? entry.Value.DeepCopy() : entry.Value); } } else { // PHP does no dereferencing when items are not merged: result.Add((deepCopy) ? entry.Value.DeepCopy() : entry.Value); } } return true; }
/// <summary> /// Retrieves an array of some keys contained in a given array. /// </summary> /// <param name="array">An array which keys to get.</param> /// <param name="searchValue">Only the keys for this value are returned. /// Values are compared using regular comparison method (<see cref="PhpComparer.CompareEq"/>).</param> /// <param name="strict">If true, uses strict comparison method (operator "===").</param> /// <returns>An array of keys being associated with specified value. /// Keys in returned array are successive integers starting from zero.</returns> /// <exception cref="PhpException"><paramref name="array"/> is a <B>null</B> reference.</exception> public static PhpArray array_keys(PhpArray array, PhpValue searchValue, bool strict = false) { if (array == null) { //PhpException.ArgumentNull("array"); //return null; throw new ArgumentNullException(); } var result = new PhpArray(); var enumerator = array.GetFastEnumerator(); if (strict) { while (enumerator.MoveNext()) { if (enumerator.CurrentValue.StrictEquals(searchValue)) result.AddValue(PhpValue.Create(enumerator.CurrentKey)); } } else { while (enumerator.MoveNext()) { if (enumerator.CurrentValue.Equals(searchValue)) result.AddValue(PhpValue.Create(enumerator.CurrentKey)); } } // no need to make a deep copy since keys are immutable objects (strings, ints): return result; }
public static PhpArray array_fill_keys(PhpArray keys, PhpValue value) { if (keys == null) { // PhpException.ArgumentNull("keys"); // return null; throw new ArgumentNullException(); } var result = new PhpArray(keys.Count); var iterator = keys.GetFastEnumerator(); while (iterator.MoveNext()) { IntStringKey key; if (Core.Convert.TryToIntStringKey(iterator.CurrentValue, out key) && !result.ContainsKey(key)) { result[key] = value; } } // makes deep copies of all added items: //result.InplaceCopyOnReturn = true; return result; }
/// <summary> /// Swaps all keys and their associated values in an array. /// </summary> /// <param name="array">The array.</param> /// <returns>An array containing entries which keys are values from the <paramref name="array"/> /// and which values are the corresponding keys.</returns> /// <remarks> /// <para> /// Values which are not of type <see cref="string"/> nor <see cref="int"/> are skipped /// and for each such value a warning is reported. If there are more entries with the same /// value in the <paramref name="array"/> the last key is considered others are ignored. /// String keys are converted using <see cref="Core.Convert.StringToArrayKey"/>. /// </para> /// <para> /// Unlike PHP this method doesn't return <B>false</B> on failure but a <B>null</B> reference. /// This is because it fails only if <paramref name="array"/> is a <B>null</B> reference. /// </para> /// </remarks> /// <exception cref="PhpException"><paramref name="array"/> is a <B>null</B> reference (Warning).</exception> /// <exception cref="PhpException">A value is neither <see cref="string"/> nor <see cref="int"/> (Warning).</exception> public static PhpArray array_flip(PhpArray array) { if (array == null) { //PhpException.ArgumentNull("array"); //return null; throw new ArgumentNullException(); } PhpArray result = new PhpArray(array.Count); var iterator = array.GetFastEnumerator(); while (iterator.MoveNext()) { var entry = iterator.Current; // dereferences value: var val = entry.Value.GetValue(); switch (val.TypeCode) { case PhpTypeCode.Int32: case PhpTypeCode.Long: case PhpTypeCode.String: case PhpTypeCode.WritableString: var askey = val.ToIntStringKey(); result[askey] = PhpValue.Create(entry.Key); break; default: // PhpException.Throw(PhpError.Warning, LibResources.GetString("neither_string_nor_integer_value", "flip")); throw new ArgumentException(); } } // no need to deep copy because values are ints/strings only (<= keys were int/strings only): return result; }
/// <summary> /// Retrieves an array of keys contained in a given array. /// </summary> /// <param name="array">An array which keys to get.</param> /// <returns><see cref="PhpArray"/> of <paramref name="array"/>'s keys. /// Keys in returned array are successive integers starting from zero.</returns> /// <exception cref="PhpException"><paramref name="array"/> is a <B>null</B> reference.</exception> public static PhpArray array_keys(PhpArray array) { if (array == null) { //PhpException.ArgumentNull("array"); //return null; throw new ArgumentNullException(); } // no need to make a deep copy since keys are immutable objects (strings, ints): var result = new PhpArray(array.Count); var enumerator = array.GetFastEnumerator(); while (enumerator.MoveNext()) { result.Add(PhpValue.Create(enumerator.CurrentKey)); } return result; }
/// <summary> /// Removes duplicate values from an array. /// </summary> /// <param name="ctx">Current runtime context.</param> /// <param name="array">The array which duplicate values to remove.</param> /// <param name="sortFlags">Specifies how the values are compared to be identical.</param> /// <returns>A copy of <paramref name="array"/> without duplicated values.</returns> /// <remarks> /// Values are compared using string comparison method (<see cref="ValueComparer.String"/>). /// </remarks> /// <exception cref="PhpException"><paramref name="array"/> is a <B>null</B> reference.</exception> //[return: PhpDeepCopy] public static PhpArray array_unique(Context ctx, PhpArray array, ComparisonMethod sortFlags = ComparisonMethod.String) { if (array == null) { //PhpException.ArgumentNull("array"); //return null; throw new ArgumentNullException(); } IComparer<PhpValue> comparer; switch (sortFlags) { case ComparisonMethod.Regular: comparer = PhpComparer.Default; break; case ComparisonMethod.Numeric: comparer = PhpNumericComparer.Default; break; case ComparisonMethod.String: comparer = new PhpStringComparer(ctx); break; case ComparisonMethod.LocaleString: //comparer = new PhpLocaleStringComparer(ctx); break; default: //PhpException.ArgumentValueNotSupported("sortFlags", (int)sortFlags); //return null; throw new ArgumentException(nameof(sortFlags)); } var result = new PhpArray(array.Count); var/*!*/identitySet = new HashSet<object>(); // get only unique values - first found using (var enumerator = array.GetFastEnumerator()) while (enumerator.MoveNext()) { if (identitySet.Add(enumerator.CurrentValue.GetValue())) { result.Add(enumerator.Current); } } //result.InplaceCopyOnReturn = true; return result; }
/// <summary> /// Counts frequency of each value in an array. /// </summary> /// <param name="array">The array which values to count.</param> /// <returns>The array which keys are values of <paramref name="array"/> and values are their frequency.</returns> /// <remarks> /// Only <see cref="string"/> and <see cref="int"/> values are counted. /// Note, string numbers (e.g. "10") and their integer equivalents (e.g. 10) are counted separately. /// </remarks> /// <exception cref="PhpException"><paramref name="array"/> is a <B>null</B> reference.</exception> /// <exception cref="PhpException">A value is neither <see cref="string"/> nor <see cref="int"/>.</exception> public static PhpArray array_count_values(PhpArray array) { if (array == null) { //PhpException.ArgumentNull("array"); //return null; throw new ArgumentNullException(); } PhpArray result = new PhpArray(); var iterator = array.GetFastEnumerator(); while (iterator.MoveNext()) { // dereferences value: var val = iterator.CurrentValue.GetValue(); IntStringKey askey; switch (val.TypeCode) { case PhpTypeCode.Int32: case PhpTypeCode.Long: case PhpTypeCode.String: case PhpTypeCode.WritableString: askey = val.ToIntStringKey(); break; default: // TODO: PhpException.Throw(PhpError.Warning, LibResources.GetString("neither_string_nor_integer_value", "count")); throw new ArgumentException(); } var countval = result[askey].ToLong(); // 0 for nonexisting entry result[askey] = PhpValue.Create(countval + 1L); } // no need to deep copy (values are ints): return result; }
/// <summary> /// Converts string keys in <see cref="PhpArray"/> to upper case. /// </summary> /// <param name="array">The <see cref="PhpArray"/> to be converted.</param> /// <returns>The copy of <paramref name="array"/> with all string keys upper cased.</returns> /// <remarks>Integer keys as well as all values remain unchanged.</remarks> internal static PhpArray StringKeysToUpper(PhpArray/*!*/ array) { if (array == null) { //PhpException.ArgumentNull("array"); //return null; throw new ArgumentNullException(); } var textInfo = System.Globalization.CultureInfo.CurrentCulture.TextInfo; // cache current culture to avoid repetitious CurrentCulture.get PhpArray result = new PhpArray(array.Count); using (var iterator = array.GetFastEnumerator()) while (iterator.MoveNext()) { var entry = iterator.Current; if (entry.Key.IsString) result[textInfo.ToUpper(entry.Key.String)] = entry.Value; else result[entry.Key] = entry.Value; } return result; }
/// <summary> /// Retrieves a slice of specified array. /// </summary> /// <param name="array">The array which slice to get.</param> /// <param name="offset">The relativized offset of the first item of the slice.</param> /// <param name="length">The relativized length of the slice.</param> /// <param name="preserveKeys">Whether to preserve integer keys. If <B>false</B>, the integer keys are reset.</param> /// <returns>The slice of <paramref name="array"/>.</returns> /// <remarks> /// See <see cref="PhpMath.AbsolutizeRange"/> for details about <paramref name="offset"/> and <paramref name="length"/>. /// </remarks> public static PhpArray array_slice(PhpArray array, int offset, int length = int.MaxValue, bool preserveKeys = false) { if (array == null) { //PhpException.ArgumentNull("array"); //return null; throw new ArgumentNullException(); } // absolutizes range: PhpMath.AbsolutizeRange(ref offset, ref length, array.Count); var iterator = array.GetFastEnumerator(); // moves iterator to the first item of the slice; // starts either from beginning or from the end (which one is more efficient): if (offset < array.Count - offset) { for (int i = -1; i < offset; i++) if (iterator.MoveNext() == false) break; } else { for (int i = array.Count; i > offset; i--) if (iterator.MovePrevious() == false) break; } // copies the slice: PhpArray result = new PhpArray(length); int ikey = 0; for (int i = 0; i < length; i++) { var entry = iterator.Current; // integer keys are reindexed if preserveKeys is false, string keys are not touched: if (preserveKeys || entry.Key.IsString) { result.Add(entry.Key, entry.Value); } else { result.Add(ikey++, entry.Value); } iterator.MoveNext(); } //result.InplaceCopyOnReturn = true; return result; }
/// <summary> /// Retrieves an array of values contained in a given array. /// </summary> /// <param name="array">An array which values to get.</param> /// <returns>A copy of <paramref name="array"/> with all keys indexed starting from zero.</returns> /// <exception cref="PhpException"><paramref name="array"/> is a <B>null</B> reference.</exception> /// <remarks>Doesn't dereference PHP references.</remarks> //[return: PhpDeepCopy] public static PhpArray array_values(PhpArray array) { if (array == null) { //PhpException.ArgumentNull("array"); //return null; throw new ArgumentNullException(nameof(array)); } // references are not dereferenced: PhpArray result = new PhpArray(array.Count); var enumerator = array.GetFastEnumerator(); while (enumerator.MoveNext()) { result.Add(enumerator.CurrentValue); } // result is inplace deeply copied on return to PHP code: //result.InplaceCopyOnReturn = true; return result; }
/// <summary> /// Searches the array for a given value and returns the corresponding key if successful. /// </summary> /// <param name="needle">The value to search for.</param> /// <param name="haystack">The <see cref="PhpArray"/> where to search.</param> /// <param name="strict">Whether strict comparison method (operator ===) is used for comparing values.</param> /// <returns>The key associated with the <paramref name="needle"/> or <B>false</B> if there is no such key.</returns> /// <exception cref="PhpException"><paramref name="haystack"/> is a <B>null</B> reference (Warning).</exception> public static PhpValue array_search(PhpValue needle, PhpArray haystack, bool strict = false) { // result needn't to be deeply copied because it is a key of an array // if (haystack == null) { // TODO: PhpException.ArgumentNull("haystack"); return PhpValue.False; } // using operator ===: if (strict) { using (var enumerator = haystack.GetFastEnumerator()) while (enumerator.MoveNext()) { // TODO: dereferences value (because of StrictEquality operator): if (needle.StrictEquals(enumerator.CurrentValue)) return PhpValue.Create(enumerator.CurrentKey); } } else { // using operator ==: using (var enumerator = haystack.GetFastEnumerator()) while (enumerator.MoveNext()) { // note: comparator manages references well: if (needle.Equals(enumerator.CurrentValue)) return PhpValue.Create(enumerator.CurrentKey); } } // not found: return PhpValue.False; }
/// <summary> /// Creates an array using one array for its keys and the second for its values. /// </summary> /// <param name="keys">The keys of resulting array.</param> /// <param name="values">The values of resulting array.</param> /// <returns>An array with keys from <paramref name="keys"/> values and values /// from <paramref name="values"/> values.</returns> /// <remarks> /// <paramref name="keys"/> and <paramref name="values"/> should have the same length (zero is /// adminssible - an empty array is returned). /// Keys are converted using <see cref="PHP.Core.Convert.ObjectToArrayKey"/> before hashed to resulting array. /// If more keys has the same value after conversion the last one is used. /// If a key is not a legal array key it is skipped. /// </remarks> /// <exception cref="PhpException"><paramref name="keys"/> or <paramref name="values"/> is a <B>null</B> reference.</exception> /// <exception cref="PhpException"><paramref name="keys"/> and <paramref name="values"/> has different length.</exception> /// <remarks>Doesn't dereference PHP references.</remarks> //[return: PhpDeepCopy] public static PhpArray array_combine(PhpArray keys, PhpArray values) { if (keys == null) { //PhpException.ArgumentNull("keys"); //return null; throw new ArgumentNullException(nameof(keys)); } if (values == null) { //PhpException.ArgumentNull("values"); //return null; throw new ArgumentNullException(nameof(values)); } if (keys.Count != values.Count) { //PhpException.Throw(PhpError.Warning, CoreResources.GetString("lengths_are_different", "keys", "values")); //return null; throw new ArgumentException(); } IntStringKey key; PhpArray result = new PhpArray(); var k_iterator = keys.GetFastEnumerator(); var v_iterator = values.GetFastEnumerator(); while (k_iterator.MoveNext()) { v_iterator.MoveNext(); // invalid keys are skipped, values are not dereferenced: if (Core.Convert.TryToIntStringKey(k_iterator.CurrentValue, out key)) { result[key] = v_iterator.CurrentValue; } } // result is inplace deeply copied on return to PHP code: //result.InplaceCopyOnReturn = true; return result; }
/// <summary> /// Pads array to the specified length with a value. /// If the length is negative adds |length| elements at beginning otherwise adds elements at the end. /// Values with integer keys that are contained in the source array are inserted to the resulting one with new /// integer keys counted from zero (or from |length| if length negative). /// </summary> /// <param name="array">The source array.</param> /// <param name="length">The length of the resulting array.</param> /// <param name="value">The value to add in array.</param> /// <returns>Padded array.</returns> /// <exception cref="PhpException">The <paramref name="array"/> argument is a <B>null</B> reference.</exception> public static PhpArray array_pad(PhpArray array, int length, PhpValue value) { if (array == null) { // PhpException.ArgumentNull("array"); // return null; throw new ArgumentNullException(); } // number of items to add: int remains = Math.Abs(length) - array.Count; // returns unchanged array (or its deep copy if called from PHP): if (remains <= 0) return array; PhpArray result = new PhpArray(array.Count + remains); // prepends items: if (length < 0) { while (remains-- > 0) result.Add(value); } // inserts items from source array // if a key is a string inserts it unchanged otherwise inserts value with max. integer key: var iterator = array.GetFastEnumerator(); while (iterator.MoveNext()) { var key = iterator.CurrentKey; if (key.IsString) result.Add(key, iterator.CurrentValue); else result.Add(iterator.CurrentValue); } // appends items: if (length > 0) { while (remains-- > 0) result.Add(value); } // the result is inplace deeply copied on return to PHP code: //result.InplaceCopyOnReturn = true; return result; }
/// <summary> /// Computes a product of all values in an array. /// Each value is converted to a number in the same way it is done by PHP. /// </summary> /// <exception cref="PhpException">Thrown if the <paramref name="array"/> argument is null.</exception> /// <returns> /// An integer, if all items are integers or strings converted to integers and the result is in integer range. /// A double, otherwise. /// </returns> public static PhpNumber array_product(PhpArray array) { if (array == null) { //PhpException.ArgumentNull("array"); //return 0; throw new ArgumentNullException(nameof(array)); } if (array.Count == 0) { return PhpNumber.Default; } PhpNumber result = PhpNumber.Create(1L); PhpNumber num; var iterator = array.GetFastEnumerator(); while (iterator.MoveNext()) { iterator.CurrentValue.ToNumber(out num); result *= num; } // return result; }
public static PhpArray preg_grep(Context ctx, string pattern, PhpArray input, int flags = 0) { if (input == null) { return null; } var result = new PhpArray(input.Count); if (input.Count != 0) { var regex = new PerlRegex.Regex(pattern); var enumerator = input.GetFastEnumerator(); while (enumerator.MoveNext()) { var str = enumerator.CurrentValue.ToStringOrThrow(ctx); var m = regex.Match(str); // move a copy to return array if success and not invert or // not success and invert if (m.Success ^ (flags & PREG_GREP_INVERT) != 0) { result.Add(enumerator.CurrentKey, enumerator.CurrentValue.DeepCopy()); } } } // return result; }
/// <summary> /// Compares two instances of <see cref="PhpArray"/> for strict equality. /// </summary> /// <param name="x">First operand. Cannot be <c>null</c>.</param> /// <param name="y">Second operand. Cannot be <c>null</c>.</param> /// <param name="incomparable">Whether arrays are incomparable /// (no difference is found before both arrays enters an infinite recursion). /// Returns <B>true</B> then.</param> static bool StrictCompareArrays(PhpArray x, PhpArray y, out bool incomparable) { Debug.Assert(x != null && y != null); incomparable = false; // if both operands point to the same internal dictionary: if (ReferenceEquals(x.table, y.table)) { return(true); // => x == y } // if numbers of elements differs: if (x.Count != y.Count) { return(false); } var iter_x = x.GetFastEnumerator(); var iter_y = y.GetFastEnumerator(); PhpArray array_x, array_y; // marks arrays as visited (will be always restored to false value before return): x._visited++; y._visited++; bool result = true; try { // compares corresponding elements (keys first values then): while (result && iter_x.MoveNext()) { iter_y.MoveNext(); // compares keys: if (!iter_x.Current.Key.Equals(iter_y.Current.Key)) { result = false; break; } var child_x = iter_x.CurrentValue; var child_y = iter_y.CurrentValue; // compares values: if ((array_x = child_x.ArrayOrNull()) != null) { if ((array_y = child_y.ArrayOrNull()) != null) { // at least one child has not been visited yet => continue with recursion: if (array_x._visited == 0 || array_y._visited == 0) { result = StrictCompareArrays(array_x, array_y, out incomparable); } else { incomparable = true; break; } } else { // an array with a non-array comparison: result = false; } } else { // compares unknown item with a non-array: result = child_x.GetValue().StrictEquals(child_y.GetValue()); } } // while } finally { x._visited--; y._visited--; } return(result); }