1

I'm using C# 8 with nullable enabled and now I'm having a problem with my regex loop:

public static async Task<IEnumerable<WorkerDto>?> GetOwnersAsync(LampContext context, string? ownerString)
{
    if (string.IsNullOrWhiteSpace(ownerString))
        return null;

    var wwids = new List<int>();

    var matches = Regex.Matches(ownerString, @"\d+");
    foreach (Match match in matches)
        wwids.Add(int.Parse(match.Value));

It's saying I have a possible null reference assignment to the match iteration variable, and I'm not sure why it would say that, or how to get around it. The documentation for Matches says it'll return an empty collection, not null.

How am I supposed to write that code now?

2
  • could you please post the initialization for ownerString & wwids Commented Nov 14, 2019 at 19:50
  • Updated the question to show ownerString is nullable. Even if I replace ownerString with "" in the Regex.Matches I'll still get the error though. Commented Nov 14, 2019 at 19:52

1 Answer 1

4

The problem is that MatchCollection implements the non-generic IList interface in preference to the generic IList<Match> interface, for backwards compatibility, and IEnumerator.Current is defined as object?, so the foreach actually casts this nullable object to a non-nullable Match. It's as if you had written

foreach (object o? in matches) {
    Match match = (Match) o;
    ...
}

All this comes from a long forgotten time before we had C# 2, and now comes back to bite us in C# 8.

There are several effective workarounds; one of the simplest is to assign matches to a variable of type IList<Match> or IEnumerable<Match> (as it does implement the generic interface):

IList<Match> matches = Regex.Matches(ownerString, @"\d+");
foreach (Match match in matches)
    wwids.Add(int.Parse(match.Value));

Note that we do not need a cast here. Another is to use .AsEnumerable() to effectively cast it:

var matches = Regex.Matches(ownerString, @"\d+");
foreach (Match match in matches.AsEnumerable())
    wwids.Add(int.Parse(match.Value));

And last but not least, if all you're doing is .Adding these values to a new list, you may as well construct the list in one go with LINQ:

var wwids = matches.Select(m => int.Parse(m.Value)).ToList();
Sign up to request clarification or add additional context in comments.

2 Comments

Great answer! Would foreach (var match in matches.OfType<Match>()) work as well? Because that's what I usually do but I haven't had the chance to use C# 8 yet.
@AhmedAbdelhameed: Sure, that works, but .OfType<> is actually a filter -- it returns all elements of that type, and leaves out others. Of course in this case they always match, but if you know this, a more effective expression of that is to use .Cast<>, which will throw if there's a mismatch. I would use neither in this case because MatchCollection actually does implement IEnumerable<Match> -- it doesn't strictly need casting, unlike collections that only implement IEnumerable.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.