tags:

views:

411

answers:

7

I made a small DLL in MSIL with two methods:

float AddNumbers(int, int)
int AddNumbers(int, int)

As some of you might know, MSIL allows you to make methods that have the same arguments as long as you have different kinds of return types (what is called return type overloading). Now, when I tried to use it from c#, as I was kinda expecting, it fired an error:

float f = ILasm1.MainClass.AddNumbers(1, 2);

The error is:

The call is ambiguous between the following methods or properties: 'ILasm1.MainClass.AddNumbers(int, int)' and 'ILasm1.MainClass.AddNumbers(int, int)'

Is c# really incapable of distinguishing between different return types? I know I cannot program methods that have as only difference different return types, but I always kinda assumed it would know how to handle it.

+14  A: 

Yes it really is not possible in C#, I know C++ does not allow this either, it has to do with the way a statement is interpreted:

double x = AddNumbers(1, 2);

The rule here is that the assignment is right-associative, meaning the expression on the right is completely evaluated first and only then is the assignment considered, applying implicit conversions where necessary.

There is no way for the compiler to determine which version is most appropriate. Using some arbitrary rule would just invite hard to find errors.

It is related to this simple statement:

double y = 5 / 2;  // y = 2.0
Henk Holterman
Nice example with the double.
RichardOD
So there is actually no way of specifying which method you want to call in C# when they are overloaded using return type?
thecoop
@thecoop: No, and that is why you can't overload by return type.
Henk Holterman
So an IL assembly that has return type overloads (say, compiled from direct IL) is impossible to use from C# :/
thecoop
@thecoop: I would think not. And pretty sure that those are not 'CLS compliant'.
Henk Holterman
+4  A: 

The problem is that there is an automatic conversion from int to float so it really doesn't know what you intended. Did you intend to call the method that takes two ints and returns an int, then convert it to float or did you intend to call the method that takes two ints and returns a float? Better to have a compiler error than to make the wrong choice and not let you know until your application breaks.

tvanfosson
MSIL can do it, lol.
devoured elysium
@devoured no, it can't. MSIL doesn't guess which one you meant; you specify which one you meant. c# has no mechanism for specifying in this case.
Rex M
A: 

How about passing in the return type as a paramater using pointers.

void AddNumbers(int a, int b, float *ret){
  *ret = (float)(a + b);
}

void AddNumbers(int a, int b, int *ret){
  *ret = (int)(a + b);
}

Calling it would becoming something like this

int a;
float b;
AddNumbers(1, 2, &a);
AddNumbers(1, 2, &b);
dharga
Yeah that's a nice idea, but not really what I asked for.
devoured elysium
C# can do that without pointers :)
vava
This is true, but as mentioned above, you can't do what you want to do. This is a work around. It solves the same problem, but in a different way. Unless there are other requirements you haven't mentioned. If there are, let's hear them and we'll see if we can improve this solution ;)
dharga
There are no requirements. I just wanted to know if it was possible or not.
devoured elysium
dharga, you would get exactly the same resolution using an `out` parameter, there is no need for [unsafe] pointers.
Henk Holterman
pointers aren't unsafe, you have unsafe programmers who don't know how to use them. but i agree that an out parameter is probably a better solution here. i'm a C programmer, so I think in pointers much quicker than other constructs ;)
dharga
dharga, the compiler won't let you use C# pointers without an [unsafe] attribute.
Henk Holterman
ugh, wow, talk about politics in a programming language.
dharga
+1  A: 

No, there's no way to do that. In fact, except in C++ with templates, no language is supporting it. This is just plainly too dangerous. And again, what if you write

var a = AddNumbers(1, 1);

what type a suppose to be?

Or what if you call it like

 double a = AddNumbers(1, 1);

or even

 AddNumbers(1, 1);

what version should it call?

Remember, there's quite complicate rules about how one type can be implicitly converted to another. Let's take a look at simple program that doesn't compile

class Program
{
    static int parse(int a) { return a; }
    static float parse(float a) { return a; }


    static void Main(string[] args)
    {
        double a = parse(1.0);
    }
}

If you try to compile it, compiler will give you an error

error C2668: 'parse' : ambiguous call to overloaded function
could be 'float parse(float)'

because 1.0 has type double and compiler really doesn't know what type to choose between int and float so it asks you to give it a hint. So you can just go ahead and convert an argument before calling a function.

But if it was return type that function is overloaded by, how do you do that then? There's simply no way of doing it.

vava
In the first case, it'd fire a warning saying "x return type assumed". Second case, would return the float version, third case it'd work just like the first.
devoured elysium
Well the big point is: if MSIL can do it, why wouldn't c# do?
devoured elysium
Have no idea why MSIL do that. Maybe you can select what function you'd like. But idea with warning just wrong. Imagine you get two functions that does slightly different side effects. Program work good until you remove one of the functions or add a third one. And then everything magically stop working and you completely lost trying to find out what happened because you'd have bugs all over the system, that will seem to be completely unrelated to the last change. That's why C++ considered to be dangerous, that's why no one will ever go with warning in case when compiler can't decide here.
vava
Henk made it clear"The rule here is that the assignment is right-associative, meaning the expression on the right is completely evaluated first and only then is the assignment considered, applying implicit conversions where necessary.". So that's why ...
arul
+2  A: 

MSIL being able to hold both methods in the same assembly has nothing to do with the compiler determining which one to call.

Covariant return types have been discussed for many years, they are partially allowed in Java, C++ has a special case, and the C# designers have added some minor changes to 4.0.

For your toy problem, there are simple typical solutions. For instance, your float AddNumbers(int, int) is likely always to be the same as (float) AddNumbers(int, int), in which case there is no need for the second function. Typically that case is handled with generics: <T> AddNumbers(<T> n1, <T> n2), so you'd end up with float AddNumbers(float, float) and int AddNumbers(int, int).

A real-life scenario is more likely to be where you have different representations you want to return but you don't want to rename the method. In the inheritance/override use case, you can also solve this somewhat with generics.

In the few cases, where I've wanted to do as you want, it's actually turned out better to name the methods more appropriately since it is more readable and maintainable in the long run.

This was also discussed already here.

The case in MSIL/C# at http://blogs.msdn.com/abhinaba/archive/2005/10/07/478221.aspx is special because they are explicit conversion operators.

Cade Roux
Nor did anyone say that.
devoured elysium
Could you highlight which bit of the linked page "hints that it might come"?
Jon Skeet
Corrected wording to mention only the 4.0 changes.
Cade Roux
+6  A: 

ECMA-334 C# Section 8.7.3

The signature of a method consists of the name of the method and the number, modifiers, and types of its formal parameters. The signature of a method does not include the return type.

You could use a generic method:

T AddNumbers<T>(int a, int b)
{
   if (typeof(T) == typeof(int) || typeof(T) == typeof(float))
   {
      return (T)Convert.ChangeType(a + b, typeof(T));
   }

   return default(T);
}
TobiasBohnen
+6  A: 

Like everyone else has said, no C# doesn't support this. In fact, the reason IL supports this, is because you have to be explicit about the return types, just like the parameters. For instance, in IL you'd say

ldarg.0
ldarg.1
call int AddNumbers(int, int)

IL doesn't really have a notion of method overloading: float AddNumbers(int, int) has no relation to int AddNumbers(int, int) whatsoever, as far as IL is concerned. You have to tell the IL compiler everything in advance, and it never tries to infer you intent (like higher level languages like C# do).

Note that most .NET languages and C# make one exception to return type overloading: conversion operators. So

public static explicit operator B(A a);
public static explicit operator C(A a);

are compiled to

public static B op_Explicit(A a);
public static C op_Explicit(A a);

Because this is such a specific corner case which has to be supported for primitive types (such as int -> bool) and reference type conversions (otherwise you'd get a very pedantic language), this is handled, but not as a case of method overloading.

Ruben