问题
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?
回答1:
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.
回答2:
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.
回答3:
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
回答4:
Function differing only with return values do not qualify for the overloading.
int x = FunctionReturnsIntOrString();
double 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.
回答5:
it is possible to get the syntax you showed out in the question, but you have to engage in quite a good amount of cleverness in order to get there.
Let's set up the problem a little more concretely. The key point here, I think, is the syntax. We want this:
int intValue = GetData();
string stringValue = GetData();
DateTime[] dateTimeArrayValue = GetData();
However, we do not want this to work:
double doubleValue = GetData();
DateTime? nullableDateTimeValue = GetData();
In order to accomplish this, we have to use an intermediary object in the return value from GetData(), whose definition looks like this:
public class Data
{
public int IntData { get; set; }
public string StringData { get; set; }
public DateTime[] DataTimeArrayData { get; set; }
public MultiReturnValueHelper<int, string, DateTime[]> GetData()
{
return new MultiReturnValueHelper<int, string, DateTime[]>(
this.IntData,
this.StringData,
this.DataTimeArrayData);
}
}
Your implementation, of course, would be quite different, but this will do. Now let's define MultiReturnValueHelper.
public class MultiReturnValueHelper<T1, T2, T3> : Tuple<T1, T2, T3>
{
internal MultiReturnValueHelper(T1 item1, T2 item2, T3 item3)
: base(item1, item2, item3)
{
}
public static implicit operator T1(MultiReturnValueHelper<T1, T2, T3> source)
{
return source.Item1;
}
public static implicit operator T2(MultiReturnValueHelper<T1, T2, T3> source)
{
return source.Item2;
}
public static implicit operator T3(MultiReturnValueHelper<T1, T2, T3> source)
{
return source.Item3;
}
}
Repeat this definition for T1, T2, T3, etc for the generic case.
You can also bind the return value helper very closely to the class or method that returns it, enabling you to create this same sort of effect for indexers, where you can get and assign a discrete set of types. That's where I've found it the most useful.
Another application is to enable syntax like the following:
data["Hello"] = "World";
data["infamy"] = new DateTime(1941, 12, 7);
data[42] = "Life, the universe, and everything";
The precise mechanism to accomplish this syntax is left as an exercise to the reader.
For a more general-purpose solution to this problem (which is, I think, the same as a discriminated union problem), please see my answer to that question.
回答6:
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.
回答7:
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.
回答8:
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?
回答9:
In C# there is no overridin return types, IL supports that kind of overriding but C# not...yet.
回答10:
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?
来源:https://stackoverflow.com/questions/937928/method-overloading-return-values