question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Add support for Span and ReadOnlySpan

See original GitHub issue

Description

Add support for Span<T> and ReadOnlySpan<T>. Though they do not directly implement IEnumerable<T> interface it would be nice to be able to assert using NotContain etc.

Complete minimal example reproducing the issue

// Arrange
ReadOnlySpan<string> span = new ReadOnlySpan<string>(new[] {"a", "b", "c"});

// Act
span = span.Slice(1);

// Assert
span.Should().NotContain("a");

Expected behavior:

Test should pass.

Actual behavior:

Compilation error saying extension method Should does not exist for ReadOnlySpan<T>.

Versions

  • Which version of Fluent Assertions are you using? 5.4.1
  • Which .NET runtime and version are you targeting? .NET Core 2.1.

Issue Analytics

  • State:open
  • Created 5 years ago
  • Comments:15 (11 by maintainers)

github_iconTop GitHub Comments

4reactions
jnyrupcommented, Sep 5, 2022

Everyone who has shown interest in assertions on Span.

2reactions
jnyrupcommented, Jun 27, 2022

Here’s an example of what we can do without converting [ReadOnly]Span<T> into T[] or string. It utilizes that Span<T> is implicitly convertible to ReadOnlySpan<T> to avoid duplicating ReadOnlySpanAssertions<T>.

Span<byte> bytes = stackalloc byte[] { 42 };
bytes.Should().NotBeEmpty()
    .And.ContainSingle()
    .Which.Should().Be(42);

ReadOnlySpan<byte> ros = stackalloc byte[] { 42 };
ros.Should().NotBeEmpty()
    .And.ContainSingle()
    .Which.Should().Be(42);
public readonly ref struct ReadOnlySpanAssertions<T>
{
    private readonly ReadOnlySpan<T> span;
        
    public ReadOnlySpanAssertions(ReadOnlySpan<T> span)
    {
        this.span = span;
    }
        
    public SpanAndConstraint<T> NotBeEmpty()
    {
        if (span.IsEmpty)
        {
            throw new Exception("Did not expect span to be empty");
        }
        
        return new(this);
    }
    
    public SpanAndWhichConstraint<T> ContainSingle()
    {
        if (span.Length != 1)
        {
            throw new Exception("Expected span to contain a single element");
        }
        
        return new(this, span[0]);
    } 
}

public readonly ref struct SpanAndConstraint<T>
{
    public ReadOnlySpanAssertions<T> And { get; }

    public SpanAndConstraint(ReadOnlySpanAssertions<T> parentConstraint)
    {
        And = parentConstraint;
    }
}

public readonly ref struct SpanAndWhichConstraint<T>
{
    public ReadOnlySpanAssertions<T> And { get; }
    
    public T Which { get; }

    public SpanAndWhichConstraint(ReadOnlySpanAssertions<T> parentConstraint, T whichValue)
    {
        And = parentConstraint;
        Which = whichValue;
    }
}

public class ByteAssertions
{
    private readonly byte subject;
    
    public ByteAssertions(byte subject)
    {
        this.subject = subject;
    }
    
    public void Be(byte expected)
    {
        if (subject != expected)
        {
            throw new Exception("Expected subject to be expected");
        }
    }
}

public static class Extensions
{
    public static ReadOnlySpanAssertions<T> Should<T>(this ReadOnlySpan<T> span) => new(span);
    
    public static ReadOnlySpanAssertions<T> Should<T>(this Span<T> span) => new(span);
    
    public static ByteAssertions Should(this byte b) => new(b);
}

SharpLab

The major downside to that approach is that we would probably have to duplicate a lot of code for a limited benefit.

The easy way out

public static class Extensions
{
    public static StringAssertions Should(this ReadOnlySpan<char> span) => span.ToString().Should();
    public static StringAssertions Should(this Span<char> span) => span.ToString().Should();
    
    public static GenericCollectionAssertions<T> Should<T>(this ReadOnlySpan<T> span) => span.ToArray().Should();
    public static GenericCollectionAssertions<T> Should<T>(this Span<T> span) => span.ToArray().Should();
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

C# - All About Span: Exploring a New .NET Mainstay
In support of Span<T> and friends, hundreds of new members and types are being added across .NET. Many of these are overloads of...
Read more >
Writing High-Performance Code Using Span<T> and ...
In this article, you'll be introduced to the new types introduced in C# 7.2: Span and Memory. I'll take a deep dive into...
Read more >
Memory<T> and Span<T> usage guidelines
Span <T> and ReadOnlySpan<T> are lightweight memory buffers that wrap references to managed or unmanaged memory. Because these types can only ...
Read more >
c# - Concatenate ReadOnlySpan<char>
I think it's worth mentioning that an overload for concatenating spans was added in .NET Core 3 and that support for .
Read more >
Improve C# code performance with Span<T>
Let's use Span<T> to obtain an array of uint from the string "163,496,691,1729" . ... The method uint.Parse(ReadOnlySpan<char>) shows that it's ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found