tags:

views:

607

answers:

9

In C# I need to be able to define a method but have it return one or two return types. The compiler gives me an error when I try to do it, but why isn't it smart enough to know which method I need to call?

int x = FunctionReturnsIntOrString();

Why would the compiler prevent me from having two functions with different return types?

+2  A: 

The return type is not part of the method's signature, only the name and the parameter types. Because of this, you can't have two methods that only differ by the return type. One way to get around this would be for your method to return an object, then the calling code must cast it to either an int or string.

However, it might be better to create two different methods, or create a class to return from the method that can either contain an int or a string.

Andy White
But in IL, the return type is used to identify the method... using ildasm you can see the retrn type specified when a callvirt instruction is specified.
It doesn't matter what's in IL - what matters is the C# specification, specifically section 3.6, where it says "The signature of a method specifically does not include the return type" . See http://msdn.microsoft.com/en-us/library/aa691131(loband).aspx
Bevan
Right -- remember, there is no such thing as "overload resolution" in MSIL. In IL, you have to always explicitly state exactly which method you are calling. That's not the case in C#.
Eric Lippert
+6  A: 

Have you though about using generics to return the correct type.

public T SomeFunction<T>()
{
    return T
}

int x = SomeFunction<int>();
string y = SomeFunction<string>();

Note: This code has not been tested

Kane
Good catch! :)
Robert Harvey
This can get get a bit tricky sometimes but if done properly is definitely the way to go. I usually go a step further and declare the function boolean, and the return object is sent by reference. This way you know when an error has occurred while avoid any casting issues. It gives you the power to do:if (!SomeFunction<string>(ref returnString)) DoErrorHandling();
Rekreativc
this code doesn't work.
Luke Schafer
+1  A: 

In C# there is no overridin return types, IL supports that kind of overriding but C# not...yet.

ArsenMkrt
+1  A: 

Because sometimes it really cannot tell which one it should then use (your example it could, but not all cases are that clear-cut):

void SomeMethod(int x) { ... }
void SomeMethod(string x) { ... }

In this context, if you call SomeMethod(FunctionReturnsIntOrString()) what should the compiler do?

jerryjvl
+6  A: 

While it may be obvious in this particular scenario, there are many scenarios where this is in fact not obvious. Lets take the following API for an example

class Bar { 
  public static int Foo();
  public static string Foo();
}

It's simply not possible for the compiler to know which Foo to call in the following scenarios

void Ambiguous() {
  Bar.Foo();
  object o = Bar.Foo();
  Dog d = (Dog)(Bar.Foo());
  var x = Bar.Foo();
  Console.WriteLine(Bar.Foo());
}

These are just a few quick samples. More contrived and evil problems surely exist.

JaredPar
You can have this same problem with overloaded parameters and the C# compiler gives you a warning. What's the problem with a warning about return types?
Please describe such an example in C# with overloaded parameters. In principle the C# compiler will allows generate an error message in the face of ambiguity. It won't just pick one and give you an error.
Dustin Campbell
@charlieday, I don't believe the ability of the C# compiler to give the warning is the issue. My *guess* is the issue is more along the lines of cost. This is a feature that has limited value and a non-trivial number of scenarios where it will outright fail (see my samples). Such features are often cut due to low benefit vs. cost. I don't work on the C# team though and cannot speak to whether or not this ever came up in a design meeting. Truthfully, I'm not even sure if it's legal at an IL level.
JaredPar
It is legal at the IL level, but there is no such thing as "overload resolution" at the IL level, so that makes it much easier. At the IL level, you always explicitly state exactly which method you are calling.
Eric Lippert
@dustin: It primarily has to do with nulls. Say you have two methods:void Foo(string x);void Foo(Bar x);And you choose to call your method like so:Foo( null );The C# compiler will give you a warning because it can't figure out which method you mean to call, so you have to change your code to accommodate:Foo( (string) null );It's an edge case, but my point is "if C# is already doing it for parameters, why not for return types".(Which has pretty much been answered already)
@charlieday -- that particular example results in an error: "The call is ambiguous between the following methods or propertyes: 'Program.Foo(string)' and 'Program.Foo(Program.Bar)'"
Dustin Campbell
The compiler almost never gives you a _warning_ if overload resolution gives an ambiguity; that is almost always an error. The situations where we give a warning in the face of ambiguity are bizarre in the extreme; here's an example: http://blogs.gotdotnet.com/ericlippert/archive/2006/04/05/odious-ambiguous-overloads-part-one.aspx
Eric Lippert
Yeah, I meant error. And my point still stands: if the compiler is already having to put up those issues, why not on return types?
+6  A: 

Function differing only with return values do not qualify for the overloading.

int x = FunctionReturnsIntOrString();
doube y = FunctionReturnsIntOrString();

In the above case compiler can identify the correct functions but consider the cases in which return values are not specified, it is ambiguous.

FunctionReturnsIntOrString();   //int version
FunctionReturnsIntOrString();   //double version

Compiler cannot resolve the overloaded methods here.

aJ
Okay... that one makes more sense... discarded return value.
+2  A: 

I think you want to seriously reconsider what you're doing and how, but you CAN do this:

int i = FunctionReturnsIntOrString<int>();
string j = FunctionReturnsIntOrString<string>();

by implementing it like this:

private T FunctionReturnsIntOrString<T>()
{
    int val = 1;
    if (typeof(T) == typeof(string) || typeof(T) == typeof(int))
    {
        return (T)(object)val;
    }
    else
    {
        throw new ArgumentException("or some other exception type");
    }
}

but there are sooo many reasons not to.

Luke Schafer
I suppose the primary reason not to do that is because it will throw an InvalidCastException at runtime if T == typeof(string).
Dustin Campbell
+1 for giving a solution, but also explaining that this is probably not a good programming practice.
ebrown
+4  A: 

From the last paragraph of section 1.6.6 of the C# 3.0 language specification:

The signature of a method must be unique in the class in which the method is declared. The signature of a method consists of the name of the method, the number of type parameters and the number, modifiers, and types of its parameters. The signature of a method does not include the return type.

In IL two methods can differ by return type alone, but outside of reflection there is no means to call a method that differs only be return type.

Timothy Carter
Thanks for the spec excerpt!
Tim Stewart
A: 

You are also not required to assign a value to a called method. For example:

int FunctionReturnsIntOrString() {return 0;}
string FunctionReturnsIntOrString() {return "some string";}

//some code
//...

int integer = FunctionReturnsIntOrString(); //It probably could have figured this one out

FunctionReturnsIntOrString(); //this is valid, which method would it call?
ebrown