-
Notifications
You must be signed in to change notification settings - Fork 1k
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
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
Proposal: Customisable overload resolution #1447
Comments
Same (in principle) as #964? |
This could lead to subtle bugs where the consumer of your API would get different overloads depending on which version of C# is in use. |
@bondsbw Not if these only came into play where there would previously have been a compile error due to overload ambiguity. |
I don't think that's the case, overload resolution is designed to be more predictable and less surprising, when it comes to backward compat, you're almost alway on your own. but I agree, OverloadResolution.cs is not long enough to be actually useful. |
@jnm2 Good point, though the examples in the proposal already have valid resolutions. |
I can see two scenarios for this issue: B) This should be prevented and only newer C# compilers allowed to compile such code.
Overload resolution has been "surprising" to me (and others I've tutored over the years) since the introduction of generics continuing to string interpolation. I think |
There have already been cases where a major version of C# changed overload resolution in places that the resolution failed to do the thing API authors expected (aka "Betterness").
If this was true, then Betterness/Better Betterness would never have happened and Bestest Betterness wouldn't even be on the table. |
@Joe4evr But doesn't "betterness" only affect code that didn't previously compile? |
Also:
This would not only make breaking changes to overload resolution itself (not just removing some candidates), but also make it customizable? That seems like a disaster. Overload resolution is already complicated as it is. But at the very least you can expect it to be the same for everyone. |
Is it really that hard to just make a new method name? |
@Neme12 the OP discuss concerns with updating existing APIs, implying that the old (less ideal) method will still exist, and duly the new (less terrible) method will be competing for the name, so this is kind of rejected by assumption for the use-cases. |
I don't think this feature is about backward compatibility, I don't want backward compatibility here, I want to explicitly change overload resolution priority when targeting a newer language version. I specifically want improved functionality by using a newer compiler. (IE: choosing the FormatedString overload opposed to the string one).
To give a more specific example: I'm developing a template based code generator based on C# 6.0 interpolated strings (If you want to see some details about it you can see here but for the purpose of the discussion I'll replicate the examples here). In the generator, FormatedString is used to enhance the features of the generator, some examples: Write($" This is a status message: {(Success, ConsoleColor.Green)}"); // this adds colorisation to output
Write("A different Message"); // /Can't have this overload ... as this is a string one, and the previous one won't work
// so instead need a new name
WriteS("A different message"); // This works, different method name, but now you can do this:
WriteS($" This is a new status message: {(Success, ConsoleColor.Green)}"); // which doesn't work because this is a string overload
//and now you have to juggle between overloads Same with logging, I would like to contextually choose to log structured and unstructured messages based on whether I use a string or a formatted string, not based on method names. |
@VisualMelon What's wrong with making a new name for the "less terrible" method? And as I understand it, it's not that the old method is "terrible" anyway. You don't always need interpolation. When you do, you call a different method that has a "Formattable" or some other suffix. And when you use that name, at least you can be sure that this is going to do the right thing. Let's say if we take the example of SQL and auto-converting interpolation to SQL parameters. In this case, you really want to be explicit about this and not rely on overload resolution (especially when it differs between different language versions!) to prevent SQL injection. With a different method name, this will also be a lot less fragile - if you accidentally pass in a string, the code won't compile. |
@popcatalin81 Why can't you make a new method called |
Also, people using |
@Neme12, the only reasons for this proposal are niceness of the API (IE: not using multiple method names) and to avoid juggling with the names in the code (The entire reason method overloads exist is for niceness, otherwise different names can be used where overloads are currently allowed) |
I'm sorry but if the overloads have a different behavior, then they should have different names. We're really going overboard with this here. There are languages that don't have method overloading at all and you can still get around just fine.
You can make more than 1 nice name. In fact a descriptive name might be much nicer than 1 that does different things depending on what you pass in, and then having to wonder what it will actually do. I don't find |
Slightly different, but I really regretted writing a couple dozen overloads named Write even though they all took different domain types because it's so much harder to Alt+\ to the one I want. |
Note: I'm fine with overloading and find it OK to have overloads for different data types as long as you can think of them as having the same behavior, eg: In the same way, I could imagine having |
IIRC the crux of the issue here isn't really overload resolution, it was an explicit design decision around string interpolation. The interpolation expression itself has no type. It has three possible implicit conversions, to |
@HaloFour I think @popcatalin81 is aware. |
@jnm2 yes, fixing the overload resolution for everyone would be a breaking change. However, making it as an option, only for some opt-in libraries, might be doable. (Especially logging libraries would opt-in to this) |
Fixing implies it's currently broken. I don't think so. Consider this piece of code: var str = $"hello {x}"; Would you want |
@Neme12 Yes! I would strongly want Also I think Example: var str = $"hello {x}";
DisplayLocalized(str); I would prefer str to be FormattableString here, as it opens up possibilities to nicer APIs like encapsulating Culture related responsibilities in other components. Other examples, include structured logging and live templating. |
I recall similar discussion: Task and ValueTask overload resolution Proposal: language support for Obsolete |
If you do, you can always specify the type yourself: FormattableString str = $"hello {x}"; But understand that this is not the majority scenario. Most of the time, people just want a regular string. |
Yes you ca do this: FormattableString str = $"hello {x}";` but if you inline your variable you also need to do this (if you have both string and formattable string overloads): DisplayLocalized((FormattableString)$"hello {x}"); |
Another (I think) great use case has popped up: The following would make method resolution chose the params overload over the int array overload: [PreferredOverload]
void Foo(params Span<int> values);
void Foo(params int[] values); Edit: fixed sample |
@HaloFour I think it should If you want your library to be both backward compatible and also have the |
Removal of the |
@HaloFour it changes the IL signature because it removes the [ParamsAttribute] from the method parameter https://msdn.microsoft.com/en-us/library/system.paramarrayattribute(v=vs.110).aspx This will break backward compatibility by making older compilers give compilation errors. |
The C# compiler won't prevent doing something that the language specification permits. |
Potentially breaking source compatibility is expected when you modify and add signatures to an object. It can always effect overload resolution.
The language specification doesn't allow using |
Would the new |
Just to clarify, is it your opinion that after using System;
using System.Collections.Generic;
public class C {
public void M(params int[] a) { }
public void M(params Span<int> a) { }
public void M(params IEnumerable<int> a) { }
} If so, which overload would be invoked for I just want to make sure that we're on the same page. |
@HaloFour It is my opinion that if we extend The proposals to extend |
Sounds like there'd be no value in applying |
@HaloFour Compatibility with older compilers/language versions? |
@jnm2 By being incompatible with newer compilers? |
@HaloFour I don't understand why it would be incompatible with newer compilers. I'd want a |
@HaloFour Wouldn't have to be the case. We could change the rules and make it smarter. |
@HaloFour We don't need a reason not to do things. What precise rule are you proposing that would forbid this set of method declarations?
@jnm2 What precise rule changes are you proposing that would make it "smarter"? Overload resolution is one of the most delicate areas of the language and we are reluctant to touch it without very good cause and proof that the change is sound. I'm not confident that we even have cause for a change here. |
@gafter The rule would be that if all the overload candidates are |
I don't disagree. I just think that it's a subpar experience to be able to have this combination of overloads marked with That said, I'm probably in agreement with @jnm2 that given the option of |
Even if other parameter positions make some other method preferable for other reasons (e.g. parameter type of
We don't even have a "plan" to add support for |
@gafter No, only if all other things are equal. The preference for
I'm answering your question to me with a question which it seems you'll inevitably have to deal with if the championed proposal #1757 is to go forward. |
I don't agree that your question is one we'll have to deal with. |
@gafter I'm sensing some kind of pushback to our questions. I apologize if I've said something out of place. |
I think this is a question that almost every language feature has to deal with. That is, for any given language feature, you basically need to ask (IMO): "Can CoreFX use this?" (or replace
I think, for the most part, the language has been fairly good in this regards (see That being said, given that |
I agree. But I also think that @jnm2 has a really good point. If the BCL were to start adding new The solution is somewhat easy. Support |
@HaloFour, right. CoreFX has some additional considerations since they can/will be used from multiple compiler versions. In that case, they may need to keep both
|
@jnm2 I'm not intending to push back against the asking of questions, I'm just not convinced in this case that an answer is needed. I'm very reluctant to modify overload resolution in general because it is one of the most delicate areas of the language. I believe that API authors can reasonably migrate as @tannergooding has suggested. This may require that program authors are capable of specifying which version of a platform API they are compiling against. If you want to use new versions of the platform APIs, you might have to upgrade both your platform version and your compiler. Of course we need to make sure that VB.Net, F#, and any other widely used languages have new versions that understand the new constructs. |
I understand that overload resolution is painfully complicated already and I wouldn't suggest modifying any existing rules. I'd suggest that the "only" (almost as bas as "just") change would be that if two overloads are equally good and one has
I disagree. The BCL can't reasonably migrate due to downlevel compiler compatibility. The same would be true with any larger project with a wide developer base. Those projects would all either be left on |
mscorlib cannot use that. String.Format cannot do that, Console.Writeline can’t do it. All of these would want to take advantage of params span |
These are the types and methods from
Some of these methods would greatly benefit having a
|
@popcatlin81 A |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
Customisable overload resolution
This is a proposal to add a customization mechanism for the C# language, to control method group overload resolution.
Summary:
C# overload resolution is designed for best possible backward compatibility in the general case. Some special types of libraries and evolving APIs meet some difficulties in implementation when their needs diverge from the C# language rules. IE: instead of being focused on compatibility they are focused on performance or improved functionality :
Motivation:
Design
[PreferredOverloadAttribute]
to designate higher priority method overloads from a valid method group.PriorityBoost
integer property to allow specifying different priorities for valid candidate methods.PriorityBoost
value, the regular overload resolution rules will apply.Formattable string example:
This is a case where a structured Logging library will indicate the preference to use the formattable string overload over the string overload in order to enhance its API usage.
C# 7.2:
unformatted
C# 7.2+:
formattable string
ISpanFormattable example:
This will allow libraries to take advantage of newer performance oriented features without introducing new method names that will require source changes, only recompilation with the latest compiler:
C# 7.2:
object
C# 7.2+:
ISpanFormattable
params Span<T> example
Champion "params Span<T>"
This will allow method overload resolution to chose the
Span<T>
based overload instead of theint[]
overload chossing the allocation free path,The text was updated successfully, but these errors were encountered: