Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is.EqualTo(double).Using(EqualityComparer<double>) causes overload ambiguity error #4916

Open
appel1 opened this issue Jan 8, 2025 · 4 comments · May be fixed by #4918
Open

Is.EqualTo(double).Using(EqualityComparer<double>) causes overload ambiguity error #4916

appel1 opened this issue Jan 8, 2025 · 4 comments · May be fixed by #4918
Labels
Investigate We will look into this

Comments

@appel1
Copy link

appel1 commented Jan 8, 2025

Updated from NUnit 4.2.2 to 4.3.2 and now some tests fail to compile.

We have tests like this where actual and expected are doubles and DoubleComparer.Approximately() is a method that returns a EqualityComparer<double>.

Assert.That(actual, Is.EqualTo(expected).Using<double>(DoubleComparer.Approximately(precision)));

This triggers this compilation error:

error CS0121: The call is ambiguous between the following methods or properties: 'IEqualWithUsingConstraintExtensions.Using<T
Expected>(IEqualWithUsingConstraint<TExpected>, IEqualityComparer<TExpected>)' and 'IEqualWithUsingConstraintExtensions.Using<TExpected>(IEqualWithUsingConstraint<TExpected>, IEqualityComparer)'

Current workaround is to introduce an explicit cast in our tests:

Assert.That(actual, Is.EqualTo(expected).Using((IEqualityComparer<double>)DoubleComparer.Approximately(precision)));
@OsirisTerje
Copy link
Member

@manfred-brands Comment ?

@OsirisTerje OsirisTerje added the Investigate We will look into this label Jan 8, 2025
@jnm2
Copy link
Contributor

jnm2 commented Jan 8, 2025

For comparison, the approach Shouldly took in a very similar scenario was to only declare the new overloads for people who were targeting .NET 9, and thus using C# 13, since C# 13 respects [OverloadResolutionPriorityAttribute]: shouldly/shouldly#984

@manfred-brands
Copy link
Member

@appel1 If your DoubleComparer.Approximately can return the interface IEqualityComparer<double> there is no ambiguity and you can even drop the <double> part.

There was ambiguity in 3.14 and 4.2.2 which you resolved by adding the <double> generic parameter, thereby excluding the non-generic IEqualityComparer overload.

The difference between 4.2 and 4.3 is that all Using overload are generic to match the type passed to EqualTo so that the trick to add the <double> to select the generic overload doesn't work.

@OsirisTerje One option would be to drop the old IEqualityComparer overload for strictly typed constraints as the whole idea was to keep the generic type not to fallback to object for comparing. But this is likely to also break (older) code.

@jnm2 The SDK is independent of the TargetFramework. For one of my work's major projects we move up the SDK to the latest to get language features, but still need to target .NET Framework 4.8 because of 3rd party libraries that are not available for .NET Core.

So we could put [OverloadResolutionPriorityAttribute] in and people only have to update the SDK to take advantage of it.
I'll try this, but the success rate for the last issue (picking object overload over string) didn't work. It always picked one or the other.

@manfred-brands
Copy link
Member

@jnm2 If I add [OverloadResolutionPriorityAttribute(1)] to the overload expecting the generic IEqualityComparer<T>, it correctly picks the generic overload if the passed parameter implements both.
However, for parameters that only implement IEqualityComparer, even if declared that way, it still wants to pick the generic overload and complains that it cannot convert the type to a IEqualityComparer<int> type. It looks like it now never considers the non-generic overload as a candidate.

{A5C4282E-3890-40F3-A89E-4C100CD0D258}

It also complains about the other Using overloads with Func.

{154F55E5-BAB0-4D19-9E0D-626B06C1C864}

I must be missing something with this overload resolution or the implementation is not quite right.
Even though the compiler complains, Inside VS when going to definition goes to the right place.
I'm using 9.0.101 SDK.

I have pushed to code to the Issue4916_OverloadResolutionPriority branch, if you want to have a look.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Investigate We will look into this
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants