mdirks
mdirks

Reputation: 73

CS0121 Ambiguous Call with Collection Initializer

I'm on a team with two other devs. For the past few months I've been working on a project in C# 12 independently, and only recently have the other devs pulled my code and run it. For one of them it worked fine, but the other faced a compilation error, CS0121, in the following context.

I have a class with two constructors. One constructor takes a string; the other takes an IEnumerable of data (instances of a record type). This class also has a public static readonly field None that wraps an empty collection of data:

public static readonly Foo None = new([]);

For myself and Colleague A, this code compiled without problem. But Colleague B could not compile the code because of a CS0121 error that the call was ambiguous between the two constructors:

The call is ambiguous between the following methods or properties: 'Foo.Foo(IEnumerable)' and 'Foo.Foo(string)'.

Colleague B changed the line to

public static readonly Foo None = new(Array.Empty<Bar>());

and was then able to compile the code.

We verified that, for all three of us, the code was set to target C# 12 and .NET 8.0

Any guesses as to what could be going on? For a hot second I wondered whether it had anything to do with breaking changes in C# 13 involving collection expressions, but we're all on C# 12, so it can't be that.

One of the comments below suggested this may be a duplicate of this question. As suggested in that question, I downgraded my SDK to 8.0.201, but was still able to compile successfully.

Here is some dummy code that mimics the issue we were having - though since I can't reproduce the error on my machine it's possible I'm missing some nuance here that causes the issue.

using System.Diagnostics;

namespace Foobar;

internal class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Running on SDK " + GetSdkVersion()); // compiles using either SDK 8.0.201 or 9.0.100
        Foo none = Foo.None;
        Console.WriteLine(none.ToString()); // prints "Foo: [ (from Bar constructor)]"

        Bar[] helloWorldBars = ["hello", "world"];
        Foo helloWorldFoo = new(helloWorldBars);
        Console.WriteLine(helloWorldFoo.ToString()); // prints "Foo: [(hello),(world) (from Bar constructor)]"

        Foo fromString = new("spam");
        Console.WriteLine(fromString.ToString()); // prints "Foo: [spam (from string constructor)]"

        Console.Write("Press ENTER to quit.");
        Console.ReadLine();
    }

    private static string GetSdkVersion()
    {
        // https://stackoverflow.com/questions/70960644/how-to-programmatically-check-the-net-sdk-version
        Process process = new();
        process.StartInfo.FileName = "dotnet.exe";
        process.StartInfo.Arguments = "--version";
        process.StartInfo.UseShellExecute = false;
        process.StartInfo.RedirectStandardOutput = true;
        process.Start();

        string version = process.StandardOutput.ReadToEnd()?.TrimEnd() ?? "UNKONWN";

        process.WaitForExit();
        return version;
    }
}

public class Foo
{
    private readonly string fooData;

    /// <summary>Returns a <see cref="Foo"/> constructed from an empty <see cref="Bar"/> collection</summary>
    public static readonly Foo None = new([]); // CS0121: The call is ambiguous for one of my colleagues
    // public static readonly Foo None = new(Array.Empty<Bar>()); // unambiguous alternative

    /// <summary>Construct a <see cref="Foo"/> from a string</summary>
    public Foo(string fooData)
    {
        this.fooData = fooData + " (from string constructor)";
    }
    /// <summary>Construct a <see cref="Foo"/> from a collection of <see cref="Bar"/>s</summary>
    public Foo(IEnumerable<Bar> bars)
    {
        this.fooData = string.Join(',', bars.Select(b => $"({b})")) + $" (from {nameof(Bar)} constructor)";
    }
    public override string ToString() => $"{nameof(Foo)}: [{fooData}]";
}

public record Bar(string BarData)
{
    public override string ToString() => BarData;
    public static implicit operator Bar(string barData) => new(barData);
}

And my global.json file:

{
  "sdk": {
    "version": "8.0.201",
    "rollForward": "disable"
  }
}

Upvotes: 1

Views: 49

Answers (0)

Related Questions