Replies: 1 comment
-
Was having this problem making stackoverflow exception because it calling recursively when I expect it to call underlying function |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
@ashmind commented on Thu Oct 01 2015
Problem
There are cases when there is need for two overloads that perform a same function over different types.
Example:
This pattern breaks when something belongs to both types, e.g.
d.GetValueOrDefault
would break ifd
is aDictionary<,>
, since it implements both interfaces. There is no good workaround other than forcing user to cast, or implementing additional overloads for common types.Another case (more of an edge case) I had recently was
which breaks on a common case of
x == null
asnull
can belong to either type.Proposal
I feel that in most of those cases it does not matter which overload will be selected for types that match both -- as long as it is actually selected and user does not have to cast.
So the suggestion is to provide a hint to the compiler -- specifying which overload to pick when all of them have exactly the same priority.
To avoid polluting the language, I suggest an attribute, e.g.:
Not sure what would be the best name -- open to feedback.
@MgSam commented on Thu Oct 01 2015
What happens in the inevitable situation when multiple methods both specify
PrimaryOverload
? You're back in the same situation as today. Maybe an integer (or double)OverloadPriority()
attribute works better. Then, like with z-order in graphics, anyone can come in and pick one that's appropriate to give them the behavior they want.@ashmind commented on Thu Oct 01 2015
Not quite. Today the API developer has no control over overload selection in cases like
IDictionary
vsIReadOnlyDictionary
. There is literally no good way for the API to provide that. What I want is to give some control to API developer, making life easier for API users (no cast required).What you are saying is API developers might mess up their chance at making things right. Well, yes, but it would be a fixable mess up, while right now there is no API-side solution.
@ashmind commented on Thu Oct 01 2015
I want to avoid this as the suggestion is only for case where overloads have exactly the same priority. Providing a numeric priority would be confusing with regards to other (e.g. type-based) priority rules.
Just to be clear, I don't want a way to prioritize my method over someone else's methods or extension methods. The main goal is to prioritise within a set of methods you own.
@MgSam commented on Fri Oct 02 2015
@ashmind You reference DLL A, which contains
TValue GetValueOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key)
. Author A has decorated it with your proposed attribute.You reference DLL B, which contains
TValue GetValueOrDefault<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dictionary, TKey key)
. Author B has also decorated it with your proposed attribute.Which one gets selected by overload resolution? You're in the same situation you are today.
Now you could say that this feature only applies to declared in the same assembly- that might be a reasonable compromise.
@ashmind commented on Fri Oct 02 2015
@MgSam You are right, two libraries can conflict. But it's not new -- it's already a conflict even without attributes. It's even likely that two authors with same goals make the same methods (
IDictionary<,>.GetValueOrDefault
in both, for example).Same as now, these conflicts are resolved by library users who decided to install both, e.g. through imports.
That would only be important if one author used the attribute, and other didn't -- not sure it's worth an extra rule. If both used the attribute, the result of ignoring attribute is same as supporting it -- ambiguity.
@gafter commented on Mon Oct 05 2015
@ashmind Can you please be more specific about what you mean by "priority"? Is this an early tie-breaker or a late tie-breaker? More generally, exactly where in the overload resolution process would this affect the result? Is it intended to apply when neither of two methods are more specific than the other, or when both of two methods are as specific as each other, or in both of these circumstances?
@ashmind commented on Mon Oct 05 2015
@gafter There are two options I considered, either of which will cover the use cases.
I think first option is probably the best, as it is the simplest, but the second one might represent the intent better.
In either option, I am not fond of the attribute name — any suggestions are welcome.
Member-level (simplest)
If we say the the attribute goes on the methods themselves, then the syntax would be
Then to handle it, in C# 5.0 Specification (haven't found 6.0), 7.5.3.2 Better function member, immediately before
we add
Parameter-level
If we say that attribute goes on the parameters, then the syntax would be
In section 7.5.3.2, we change:
to
@gafter commented on Mon Oct 05 2015
@ashmind In both options, you are adding a new bullet under the paragraph
Since P1 is IDictionary and Q1 is IReadOnlyDictionary, the sequences are not equivalent. Therefore neither change would help in the use case that motivated the proposal.
You've also messed up the intended meaning of this section, which was to provide a tie breaker when two identical method signatures are only identical because of instantiation and expansion of the method and enclosing types. None of that applies in your use case.
@ashmind commented on Mon Oct 05 2015
@gafter Hmm, you are right about the equivalence part.
I'm not sure about intended meaning of the whole section though -- seems like section overall defines which function is better for two applicable functions (applicable as per 7.5.3.1). What do you mean by "identical" in that case?
Based on the above, does the following make sense?
Member-level
At the very end of 7.5.3.2, add the following:
If none of the preceding rules identifies MP as better than MQ, and MP is annotated with BetterOverloadAttribute, and MQ is not annotated with BetterOverloadAttribute, then MP is better than MQ.
(That does not go as a list item to the "identity conversion" list, just below it)
@ashmind commented on Mon Oct 05 2015
Parameter-level (alternative)
Replace
With
Given an argument list A with a set of argument expressions { E1, E2, ..., EN } and two applicable function members MP and MQ with parameters { P1, P2, ..., PN } and { Q1, Q2, ..., QN }, MP is defined to be a better function member than MQ if
@gafter commented on Mon Oct 05 2015
@ashmind Re "I'm not sure about intended meaning of the whole section though"
That section is for when two overloads that were originally different collide (become identical) because of generic instantiation and/or
params
expansion.@ashmind commented on Mon Oct 05 2015
@gafter OK I see the confusion, I just understood your "section" term as referring to the whole of 7.5.3.2 and so was confused as it's not just about identical overloads. Now I see it, thanks.
Anyway, does my new spec make sense?
@jnm2 commented on Mon Nov 21 2016
I want this so bad. I have the exact same
IReadOnlyDictionary
vsIDictionary
extension method pain.Is it possible to need a tiebreaker between three overloads but at the same time need another tiebreaker between just two of them for a different call site? If so we'll need a priority parameter. (yuck)
@ashmind commented on Mon Apr 10 2017
@gafter Is there an automatic way to migrate this to dotnet/csharplang?
@jnm2 commented on Tue Oct 03 2017
@ashmind Anyone can migrate this with ZenHub or a similar tool. Do you want to, or shall I?
Beta Was this translation helpful? Give feedback.
All reactions