[Proposal] Ambiguous Overload Resolution Priority Hints #4867
Replies: 6 comments 15 replies
-
Wouldn't this mean adding new classes with these attributes can silently change the meaning of my code? given a type heirarchy like: public interface ISomething<T>
{
T this[int index] { get; }
}
public interface ISomethingElse<T>
{
T this[int index] { get; }
}
public interface ITheOtherGuy<T>
{
T this[int index] { get; }
}
public class MyWeirdType<T> : ISomething<T>, ISomethingElse<T>, ITheOtherGuy<T>
{
public T this[int index] => default(T);
} Version 1: using System;
public class C {
public void M() {
var list = new MyWeirdType<int>();
list.WriteFirstItem(); // picks ISomething<T>
}
}
public static class ISomethingExtensions
{
[OverloadPriority(1)]
public static void WriteFirstItem<T>(this ISomething<T> collection)
{
Console.WriteLine(collection[0]);
}
}
public static class ISomethingElseExtensions
{
[OverloadPriority(0)]
public static void WriteFirstItem<T>(this ISomethingElse<T> collection)
{
Console.WriteLine(collection[0]);
}
} Version 2 or Someone adds a nuget package etc...: using System;
public class C {
public void M() {
var list = new MyWeirdType<int>();
list.WriteFirstItem(); // picks ITheOtherGuy<T> silently
}
}
public static class ITheOtherGuyExtensions
{
[OverloadPriority(2)]
public static void WriteFirstItem<T>(this ITheOtherGuy<T> collection)
{
throw new InvalideOperationException();
}
}
public static class ISomethingExtensions
{
[OverloadPriority(1)]
public static void WriteFirstItem<T>(this ISomething<T> collection)
{
Console.WriteLine(collection[0]);
}
}
public static class ISomethingElseExtensions
{
[OverloadPriority(0)]
public static void WriteFirstItem<T>(this ISomethingElse<T> collection)
{
Console.WriteLine(collection[0]);
}
} This would mean that any change in type visibility could silently change which overloads are chosen for a given method. |
Beta Was this translation helpful? Give feedback.
-
Just want to put DefaultOverloadAttribute and OverloadAttribute be used in Windows Runtime API into picture. |
Beta Was this translation helpful? Give feedback.
-
I'd love to see something like this added to the compiler to help resolve method ambiguities. Currently, I use the approach of "further away" namespaces to lower the priority of more generic methods: namespace A.B.C.D
{
public static class EnumerableExtensions
{
public static IFoo<T> GetFoo<T>(this IEnumerable<T> item) => new EnumerableFoo<T>(item);
}
}
namespace A.B.C
{
public static class ListExtensions
{
public static IFoo<T> GetFoo<T>(this IList<T> item) => new ListFoo<T>(item);
}
} Being able to do away with multiple files in multiple namespaces and put it all in one file would be a lot tidier as it always feels like a hack to make use of namespaces like this: namespace A.B.C
{
public static class AllOfTheExtensions
{
[OverloadPriority(0)]
public static IFoo<T> GetFoo<T>(this IEnumerable<T> item) => new EnumerableFoo<T>(item);
[OverloadPriority(1)]
public static IFoo<T> GetFoo<T>(this IList<T> item) => new ListFoo<T>(item);
}
} |
Beta Was this translation helpful? Give feedback.
-
Doesn't shapes solve this problem? public void WriteFirstItem<T>(this T collection) where T : Indexable
{
Console.WriteLine(collection[0]);
} Or just add In my opinion |
Beta Was this translation helpful? Give feedback.
-
This feature was precisely what I came here to propose. I recently brought it up on Stack Overflow and it was not well received there, but the more I think about it, the more I think it could be a nice feature. The primary arguments against it are
Yes, both of those are true. We DO have work-arounds that make this not strictly necessary and if you abuse it, you could cause problems, but aren't both of those statements true about an awful lot of other things that wind up getting implemented, anyway? Doesn't the ability to write code that is cleaner, more concise, and/or easier to consume very often warrant new features? As for the second concern, there are far more "dangerous" features that have been around forever (implicit operator overloading comes to mind) that are there because when used correctly, they make things much easier. If a user has gone to the effort of adding an overload resolution hint, then it is safe for the compiler to assume they have thought about it and have concluded that it is the best move and at this point, any bugs introduced are the responsibility of the developer. This argument feels a bit like not being able to buy a saw at a hardware store because you might cut yourself with it. (Side note: I've been trying to come up with a use case where this would cause problems, but would not have been obvious during coding and have not been able to think of anything. Either there is no problem at all, or you practically have to be trying to cause a problem.) It is a common programming pattern to have a single function that does some work for you and then to add several overloads that accept a different shape of data as input and simply transform that input into a shape that the original function can accept. This gives the consumer of the function a single method name to remember when trying to perform that action. As it is now, once there is an ambiguity, they have to remember slight variations on the name of the function, transform the data input at the call, pass in parameters that could otherwise have been optional, or do some other similar not-a-big-deal-but-could-be-better actions. At least in this pattern, if there is ever an ambiguity between any of these overloads, then it usually doesn't really matter which path it takes on its way to the main function because ambiguity implies that any of the candidates could transform the data into a form it can understand. |
Beta Was this translation helpful? Give feedback.
-
There are some situations where overloads can be ambiguous to the compiler but there is a preference as to which one should be used if both are possible. I propose an
OverloadPriorityAttribute
that can be used in these situations to pick an overload that should be favored. For example:Another situation where this would be useful is situations like this:
Overload priorities should only be considered when disambiguating methods within the same assembly to prevent cross-library competing for higher priorities. If a method is ambiguous between methods in different assemblies, the ambiguity should remain.
This should also be usable for extension methods:
@333fred @jmarolf @CyrusNajmabadi
Beta Was this translation helpful? Give feedback.
All reactions