Monday, December 4, 2017

ISerializer with Span<T>

While trying out the new Span<T> type from System.Memory package I encountered an error:

   typeof(System.InvalidProgramException): Common Language Runtime detected an invalid program.

At first I thought that must be a bug since System.Memory package is still a preview release but it turns out that it's by design.

I was experimenting with using ReadOnlySpan<T> instead of byte[] on Greentube.Serialization.

The contract became:

Once I fixed the compiler related problems, I ran all tests but 6 of them failed:

After learning a bit more about Span<T>, mostly thankfully to great documentation from Adam Sitnik I was able to understand what was going on.

Firstly the Assert.Throws<T> case:

The problem here is that the compiler will resolve () => sut.Serialize(object)) as Func<ReadOnlySpan<byte>>. That's unfortunately illegal since Span<T> (and ReadOnlySpan<T>) are stack-only types. It can't end up on the heap so it's not allowed to be a generic type.
I'm not going to try to explain better than Adam Sitnik so please refer to his doc for more.

The fix:

The work around here is simple enough. I've just added: { }

What the compiler resolves it as now is a simple Action since I'm ignoring the return of the Serialize method which is of type ReadOnlySpan<T>. Now the test passes again.

I've also changed the implementation from a null check to a default value check since Span is a value type. Now the exception thrown is ArgumentException instead.

The other problem was related to NSubstitute. Having a subtitute of ISerializer is a problem since internally NSubstitute will keep a reference to it which would require boxing the argument of type ReadOnlySpan<T> and that results on the same 'Common Language Runtime detected an invalid program.' error.

To solve that, I used a Stub instead of an NSubstitute mock.

No comments:

Post a Comment