Limited return-type overloading

Topics: C# Language Design, VB Language Design
Nov 22, 2014 at 6:54 PM
C# requires that method overloads be determined entirely by arguments, and does not allow it to be performed based upon return type. Although adding "fully-general" return-type overloading will either create ambiguities or require complex language rules to indicate how all the potential ambiguities should be resolved, there are nonetheless a few situations where the use of a return type could be considered during overload evaluation without creating ambiguity.

The cases which would be simplest to detect and probably the most useful would occur in situations where either:
  1. A the return value of method m() is simply ignored
  2. A the return value of method m() is immediately cast or coerced to some other type.
  3. A the return value of method m() is used as the Kth argument out of N in a method call, and in all of the N-parameter overloads in the method group, the Kth argument is either one particular type, or else is of a type to which no possible return type for message m() could be implicitly convertible.
If for every (method group, argument signature) combination it were possible to mark one overload as "preferred", to be used in all cases where none of the above conditions apply, then the complexity associated with more general return-value overloading could be avoided but it would still be possible to define things like:
static Int32 Int32Sum(Int32 a, Int32 b)
{ return checked(a+b); }

static In64 Sum(Int32 a, Int32 b)
{ return (Int64)a+(Int64)b;
and have a statement like long l=Sum(a,b); or double d=Sum(a,b); perform the computation as Int64, but allow code to also say Int32 i=Sum(a,b) and have the computation performed as Int32. The use of the AlternateOverload attribute would instruct the compiler that it should consider Int32Sum as an overload for Sum if it can perform return-type overloading and the return value is being cast or coerced to Int32, but that compilers which don't understand such things should not be confused by it.

Another usage scenario would be:
String DoSomething() { Do some action and return a string }

void DoSomething() { Do some action and return nothing }
It's not uncommon for methods to returning values which some callers care about but many callers ignore. In some such cases, the computation of the value in question may have significant direct costs (and omitting the computation would eliminate them). In others, computing the return value may require an object to eagerly process a request which might otherwise be deferred and consolidated with future requests. As a simple example, suppose a type had an AppendToCommandString method which returns a String containing everything appended thus far. If the method is called a dozen times by code which is going to ignore the intermediate string value, the method might profit by adding passed-in values to a String[] rather than concatenating them all to a String, and only performing the actual concatenation when there's a need for a single String containing all the data.

It would have been nice if when CLS was defined, it had specified attributes which would instruct compilers that certain overloads should only be used if the compiler could use return-value-overloading rules to determine that they were better than other overloads with identical parameter signatures. Since it wasn't, I think it would be necessary to allow overloads to be given names which differ from other members of the group. That's a little ugly, but if the recognized proper use of such overloads is to write methods which behave semantically as though the primary overload were called and the return type coerced or cast to that of the alternative overload, the fact that such overloads would only be seen by compilers that know to look for them shouldn't be a problem.