19

在 C# 中,我需要能够定义一个方法,但让它返回一个或两个返回类型。当我尝试这样做时,编译器给了我一个错误,但为什么它不够聪明,无法知道我需要调用哪个方法?

int x = FunctionReturnsIntOrString();

为什么编译器会阻止我拥有两个具有不同返回类型的函数?

4

10 回答 10

26

C# 3.0 语言规范第 1.6.6 节的最后一段:

方法的签名在声明该方法的类中必须是唯一的。方法的签名由方法的名称、类型参数的数量及其参数的数量、修饰符和类型组成。方法的签名不包括返回类型。

在 IL 中,两个方法可以仅因返回类型而不同,但在反射之外,没有办法调用仅返回类型不同的方法。

于 2009-06-02T05:03:41.457 回答
22

虽然在这种特定情况下可能很明显,但在许多情况下这实际上并不明显。让我们以下面的 API 为例

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

编译器根本不可能知道在以下情况下调用哪个 Foo

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

这些只是一些快速示例。肯定存在更多人为和邪恶的问题。

于 2009-06-02T04:52:35.540 回答
10

您是否考虑过使用泛型返回正确的类型。

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

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

注意:此代码未经测试

于 2009-06-02T04:51:33.453 回答
9

仅与返回值不同的函数不符合重载条件。

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

在上述情况下,编译器可以识别正确的函数,但考虑到未指定返回值的情况,这是不明确的。

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

编译器无法解析此处的重载方法。

于 2009-06-02T04:53:22.547 回答
4

可以获得您在问题中显示的语法,但您必须非常聪明才能到达那里。

让我们更具体地设置问题。我认为这里的关键点是语法。我们想要这个:

int intValue = GetData();
string stringValue = GetData();
DateTime[] dateTimeArrayValue = GetData();

但是,我们不希望这样做:

double doubleValue = GetData();
DateTime? nullableDateTimeValue = GetData();

为了实现这一点,我们必须在 from 的返回值中使用一个中间对象GetData(),其定义如下所示:

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);
    }
}

当然,您的实现会完全不同,但这会发生。现在让我们定义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;
    }
}

对一般情况重复此定义,对T1, T2,T3等。

您还可以将返回值帮助程序非常紧密地绑定到返回它的类或方法,使您能够为索引器创建同样的效果,您可以在其中获取和分配一组离散的类型。那是我发现它最有用的地方。

另一个应用程序是启用如下语法:

data["Hello"] = "World";
data["infamy"] = new DateTime(1941, 12, 7);
data[42] = "Life, the universe, and everything";

完成此语法的精确机制留给读者作为练习。

有关此问题的更通用解决方案(我认为,这与有区别的联合问题相同),请参阅我对该问题的回答

于 2011-04-30T06:43:12.820 回答
3

我认为您想认真地重新考虑您在做什么以及如何做,但是您可以这样做:

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

通过像这样实现它:

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");
    }
}

但有很多理由不这样做。

于 2009-06-02T05:03:03.000 回答
2

返回类型不是方法签名的一部分,只有名称和参数类型。正因为如此,您不能有两个仅在返回类型上有所不同的方法。解决此问题的一种方法是让您的方法返回一个对象,然后调用代码必须将其转换为 int 或 string。

但是,最好创建两个不同的方法,或者创建一个类以从可以包含 int 或字符串的方法返回。

于 2009-06-02T04:51:10.940 回答
2

因为有时它真的不能告诉它应该使用哪一个(你的例子可以,但并非所有情况都那么明确):

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

在这种情况下,如果调用SomeMethod(FunctionReturnsIntOrString())编译器应该怎么做?

于 2009-06-02T04:51:51.597 回答
1

在 C# 中没有覆盖返回类型,IL 支持这种覆盖,但 C# 还没有。

于 2009-06-02T04:51:35.277 回答
1

您也不需要为被调用的方法赋值。例如:

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?
于 2009-06-02T05:11:19.060 回答