9

I have two versions of helper methods for logging on ILogger. The first (classic extension methods) compiles fine; the second using extension members results in a compiler warning/error code.

CS8620: Argument of type ‘(string, object EntityPath)’ cannot be used for parameter ‘scopes’ of type ‘(string key, object? value)[]

Working version:

public static class LoggerExtensions
{
    public static IDisposable? BeginKeyValueScope(this ILogger logger,
        params (string key, object? value)[] scopes)
    {
        return logger.BeginScope(
            new Dictionary<string, object?>(scopes.Select(x =>
                new KeyValuePair<string, object?>(x.key, x.value))));
    }

    public static IDisposable? BeginAzureServiceBusReceptionScope(this ILogger logger, ProcessMessageEventArgs args)
    {
        return logger.BeginKeyValueScope(
            ("AzureServiceBusEntityPath", args.EntityPath),
            ("AzureServiceBusMessageId", args.Message.MessageId)
        );
    }
}

Failing version using extension‐members:

public static class LoggerExtensions
{
    extension(ILogger logger)
    {
        public IDisposable? BeginKeyValueScope(params (string key, object? value)[] scopes)
        {
            return logger.BeginScope(
                new Dictionary<string, object?>(scopes.Select(x =>
                    new KeyValuePair<string, object?>(x.key, x.value))));
        }

        public IDisposable? BeginAzureServiceBusReceptionScope(ProcessMessageEventArgs args)
        {
            return logger.BeginKeyValueScope(
                ("AzureServiceBusEntityPath", args.EntityPath),
                ("AzureServiceBusMessageId", args.Message.MessageId)
            );
        }
    }
}

Why does the extension-members version trigger CS8620 while the classic version does not?

Is this a compiler limitation/bug (less likely) with the new extension‐members syntax or am I doing something wrong (much more likely).

4
  • x.value is an object, not an object?. The code already had a problem. Post the full compiler error and look at the actual line the error points to. I suspect it's new KeyValuePair<string, object?>(x.key, x.value) Commented 21 hours ago
  • 2
    Lesson to be learned here: at least two people have been focusing on the implementation of the extension method rather than the callsite, which is where the warning is. 1) Make your repros minimal (see my answer for a simpler example); 2) show where any warnings are being reported. Commented 21 hours ago
  • I'll emphasize Post the full compiler error and look at the actual line the error points to. because I'm just guessing that x.value could be involved, because that's the only place where I see the possibility of an object. I'd have to invent code to reproduce any problems, and end up with a no-repro or the wrong problems Commented 21 hours ago
  • @PanagiotisKanavos: The error message explicitly talks about the scopes parameter though, and that a value can't be used for it - which strongly suggests it's the call to BeginKeyValueScope rather than the call to BeginScope orSelect that's failing. But I certainly agree that the less that's left to the imagination/deduction, the better. Commented 20 hours ago

2 Answers 2

9

I believe this is a bug in the compiler around the use of params. It's not a limitation mentioned in the feature page.

It's easy to reproduce this without the complexity of either tuples or external dependencies:

Extensions.cs:

namespace Q79826396;

public static class Extensions
{
    public static void OldMethod(this object x, params string[] scopes) { }

    extension(object obj)
    {
        public void NewMethod(params string[] scopes) {}
    }
}

Program.cs:

using Q79826396;

object obj = new object();

obj.OldMethod("x", "y");
// Warning (not an error, but a warning)
obj.NewMethod("x", "y");
// No warning
obj.NewMethod(new[] { "x", "y" });

The warning itself is very odd:

warning CS8620: Argument of type 'string' cannot be used for parameter 'scopes' of type 'string[]' in 'void extension(object).NewMethod(params string[] scopes)' due to differences in the nullability of reference types.

Nothing is nullable as far as I can see. Changing the parameter declaration to any of string?[], string[]? or string?[]? doesn't help.

It looks like this has already been reported and marked fixed - but as users are reporting that they're still seeing it in .NET 10 GA, hopefully it will be reopened and fixed again.

Sign up to request clarification or add additional context in comments.

1 Comment

Specifically, the fix was applied to the main branch, but not to the release branch, and, though reported a couple weeks before the final release day, it did not get backported to the release branch in time for the GA.
-3

I did a search on the error code CS8620 and according to https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/nullable-warnings#mismatch-in-nullability-declaration

Argument cannot be used for parameter due to differences in the nullability of reference types.

As to why the compiler thinks that there is a type mismatch....

This is speculation on my part - so I could be way off base here - but what is the result of scopes.select(....)?

So in this instance I would break the lamba expression into its constituent parts and see what the compiler thinks the types are. So is the compiler trying to create a Dictionary<string, string?> or is it actually creating a Dictionary<string, object?>?

i.e. what does the following code compile to:


public static class LoggerExtensions
{
    extension(ILogger logger)
    {
        public IDisposable? BeginKeyValueScope(params (string key, object? value)[] scopes)
        {
            var scopeDictionaryValues = scopes.Select(x => new KeyValuePair<string, object?>(x.key, x.value); // <--- How has the compiler defined scopeDictionaryValues? 

            return logger.BeginScope(
                new Dictionary<string, object?>(scopeDictionaryVAlues)));
        }

        public IDisposable? BeginAzureServiceBusReceptionScope(ProcessMessageEventArgs args)
        {
            return logger.BeginKeyValueScope(
                ("AzureServiceBusEntityPath", args.EntityPath),
                ("AzureServiceBusMessageId", args.Message.MessageId)
            );
        }
    }
}

4 Comments

It's nothing to do with the implementation of the extension method - it's calling the method that's causing the warning. See my answer for a simplified repro.
But did you search the error code? Also you failed to mention that the issue was when you CALLED the code. From the way you presented the issue it looked to me as though it was to do with the IMPLEMENTATION of the method not when you were calling it.
"Also you failed to mention" - it wasn't my question. I didn't "present" the issue at all. But I did look at the warning message carefully, and saw that it related to providing a value for the scopes parameter - which is a parameter of the extension method. Neither Select nor BeginScope has a parameter called scopes. What I also did was to reproduce the issue myself using the code in the question, so that I could see where the warning actually occurs - before creating a simpler repro in my answer.
The warning is reported at logger.BeginKeyValueScope(...), your speculation is heading in the wrong direction.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.