方法重载返回值

Posted

技术标签:

【中文标题】方法重载返回值【英文标题】:Method overloading return values [duplicate] 【发布时间】:2010-10-30 14:10:41 【问题描述】:

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

int x = FunctionReturnsIntOrString();

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

【问题讨论】:

charlieday,您能否提供更多详细信息,说明您为什么需要一个可以返回多种返回类型的方法?我想知道 System 命名空间中的 Convert 类是否是一个很好的模型。它有多种不同返回类型的转换方法,但是它将返回类型编码为方法的名称。例如。 ToBoolean、ToByte、ToChar 等 原因是因为在您的场景中,类型信息将“双向”流动。语言设计的一个基本原则是你应该能够独立地分析表达式的每个部分的类型。我们希望能够独立于它被分配给什么来确定调用表达式的类型。此规则的例外是 lambda 表达式。在 lambda 表达式中使类型信息“双向流动”非常困难。有关详细信息,请参阅我的博客中的“匿名方法与 lambda 表达式”。 你可以在这里查看我的答案***.com/questions/1366136/… 【参考方案1】:

来自C# 3.0 language specification第1.6.6节的最后一段:

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

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

【讨论】:

“方法的签名不包括返回类型”,这一行启发了我。【参考方案2】:

虽然在这种特殊情况下可能很明显,但在许多情况下实际上并不明显。我们以下面的 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());

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

【讨论】:

重载参数也会出现同样的问题,C# 编译器会发出警告。关于返回类型的警告有什么问题? 请用 C# 描述一个带有重载参数的例子。原则上,C# 编译器允许在遇到歧义时生成错误消息。它不会只选择一个并给你一个错误。 @charlieday,我不相信 C# 编译器发出警告的能力是问题所在。我的猜测 是更多的成本问题。这是一个价值有限的功能,并且在很多情况下它将彻底失败(请参阅我的示例)。由于收益与成本较低,此类功能通常会被削减。不过,我不在 C# 团队工作,也无法谈论这是否曾出现在设计会议上。老实说,我什至不确定这在 IL 级别是否合法。 这在 IL 级别是合法的,但在 IL 级别没有“重载解决”之类的东西,因此更容易。在 IL 级别,您始终明确说明您正在调用的方法。 @dustin:它主要与空值有关。假设您有两种方法: void Foo(string x);无效Foo(酒吧x);你选择这样调用你的方法: Foo( null ); C# 编译器会给您一个警告,因为它无法确定您要调用哪个方法,因此您必须更改代码以适应: Foo( (string) null );这是一个极端情况,但我的观点是“如果 C# 已经在为参数做这件事,为什么不做返回类型”。 (这几乎已经得到了回答)【参考方案3】:

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

public T SomeFunction<T>()

    return T


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

注意:此代码未经测试

【讨论】:

这有时会变得有点棘手,但如果做得好绝对是正确的选择。我通常会更进一步,将函数声明为布尔值,并通过引用发送返回对象。这样您就可以知道何时发生错误,同时避免任何转换问题。它赋予您执行以下操作的能力: if (!SomeFunction(ref returnString)) DoErrorHandling(); 此代码不起作用。【参考方案4】:

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

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

在上述情况下编译器可以识别出正确的函数,但考虑到没有指定返回值的情况,这是模棱两可的。

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

编译器无法解析这里的重载方法。

【讨论】:

好吧...那个更有意义...丢弃的返回值。【参考方案5】:

有可能得到你在问题中显示的语法,但你必须非常聪明才能做到这一点。

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

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

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

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

为了实现这一点,我们必须在来自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;
    

对于一般情况,对 T1T2T3 等重复此定义。

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

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

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

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

对于这个问题的更通用的解决方案(我认为这与歧视联合问题相同),请参阅my answer to that question。

【讨论】:

之前处理过这样的代码:data["Hello"] = "World"; data["infamy"] = new DateTime(1941, 12, 7); data[42] = "Life, the universe, and everything"; 我不想再与它有任何关系。如果您需要这样的语法,只需假设您做错了,应该考虑使用类型化对象。【参考方案6】:

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

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

但有很多理由不这样做。

【讨论】:

我想不这样做的主要原因是因为如果 T == typeof(string) 会在运行时抛出 InvalidCastException。 +1 用于提供解决方案,但也说明这可能不是一个好的编程习惯。 虽然这听起来很糟糕,但我想知道您能否列举出不应该编写这样的代码的原因?【参考方案7】:

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

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

【讨论】:

但是在IL中,返回类型是用来标识方法的……使用ildasm可以看到指定callvirt指令时指定的retrn类型。 IL 中的内容无关紧要 - 重要的是 C# 规范,特别是第 3.6 节,其中说“方法的签名特别不包括返回类型”。见msdn.microsoft.com/en-us/library/aa691131(loband).aspx 对——记住,MSIL 中没有“过载解决”之类的东西。在 IL 中,您必须始终明确说明您正在调用的方法。在 C# 中情况并非如此。【参考方案8】:

因为有时它确实无法确定应该使用哪一个(您的示例可以,但并非所有情况都如此明确):

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

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

【讨论】:

【参考方案9】:

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

【讨论】:

【参考方案10】:

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

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?

【讨论】:

以上是关于方法重载返回值的主要内容,如果未能解决你的问题,请参考以下文章

方法的重写与重载的区别(Override与Overload)。重载的方法是否可以改变返回值的类型

方法重载返回值

重载和重写

方法的重写和重载

重载和重写的区别

为啥不调用具有 const 引用返回值的重载方法?