Array to ISequence<T> will miss null items on iOS

public static class Program
{
    public int Main(int argc, AnsiChar** argv)
    {
        var a = new string[]{"a",null,"c"};
        string log = "#1 ";
        foreach(var item in a){
            log += $"{item} ";
        }
        NSLog(log); // output: #1 a (null) c 

        printSeq(a);
    }

    public void printSeq<T>(ISequence<T> seq){
        string log = "#2 ";
        foreach(var item in seq){
            log += $"{item} ";
        }
        NSLog(log); // output: #2 a c
    }
}

And I’ve checked the code of ArrayUtils.idArrayToSequence:

class method ArrayUtils.idArrayToSequence(val: array of id): INSFastEnumeration;
begin
for i: Integer := 0 to length(val) -1 do
yield val[i];
end;

It seems the cause is that yield will skip null items. I’m not sure if it’s a bug or by design.

And this must be the cause of another bug I reported months ago:

Thanks, logged as bugs://73593

bugs://73593 got closed with status notfixable.

I’m afraid this is not fixable, since Cocoa’s INSE numerable infrastructure does dot support nil. (nil means end of list).

I have a solution: make a ArraySequence to handle nill issue for C style array.
Here’s the code:

public static class TestClass {
    public static void test() {
        var array = new string[] {"a", null, "b", null, "c", null, "d", null, "e", null};
        // iterateSeq(array);
        iterateSeq(idArrayToSequence(array));
    }

    public static void iterateSeq<T>(ISequence<T> seq) {
        foreach (var item in seq) {
            NSLog($"item: {item}");
        }
    }

    public static INSFastEnumeration<T> idArrayToSequence<T>(T[] array) {
        return new ArraySequence<T>(array);
    }
}

public class ArraySequence<T> : INSFastEnumeration<T> {

    private readonly T[] mArray;

    public ArraySequence(T[] array) {
        mArray = array;
    }

    public NSUInteger countByEnumeratingWithState(NSFastEnumerationState* state) objects(T* buffer) count(NSUInteger bufferLen) {

        // 1) check
        if (state->state == 0) {
            state->state = 1;
            state->mutationsPtr = (NativeUInt*)(&mArray);
        }

        // 2) return
        var array = mArray;
        var arrayLen = array.length;
        var arrayIndex = state->state - 1;
        var bufferIndex = 0;
        while (arrayIndex < arrayLen && bufferIndex < bufferLen) {
            buffer[bufferIndex++] = array[arrayIndex++];
        }
        state->itemsPtr = (id*)buffer;
        state->state = arrayIndex + 1;
        return bufferIndex;
    }
}

Has anyone investigated my solution as above?

bugs://73593 got reopened.

Still no feedback…It’s simple to just replace your ArrayUtils.idArrayToSequence with my idArrayToSequence and check if it can pass all unit tests.

But it’s not that simple, we need to make sure iterators work with null somehow, and this requires more than a simple patch of idArrayToSequence.

Isn’t the existing behaviour correct, which as I understand it, skips null values?

If we consider null to mean “no value” then why should they be returned?

Is that means the yield implementation for Cocoa should be fixed instead of a specific patch for array? I can’t agree more actually. But if that thing is not fixable (I’m not sure), we can only patch it.

It’s true for NSArray but here is common array which certainly supports null values. So a conversion from array to ISequence should not break this.

And for cross platform purpose, we should have a collection library supporting null values as Sugar does.

Which is why I didn’t replace the code with what you wrote here but instead will spend some time thinking on how to best solve this.