真的不可能使用返回类型重载?

时间:2009-09-26 12:45:00

标签: c# .net

我用两种方法在MSIL中创建了一个小DLL:

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

正如你们中的一些人可能知道的那样,MSIL允许你创建具有相同参数的方法,只要你有不同类型的返回类型(所谓的返回类型重载)。现在,当我试图从c#中使用它时,正如我所期待的那样,它引发了一个错误:

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

错误是:

  

以下方法或属性之间的调用不明确:   'ILasm1.MainClass.AddNumbers(int,int)'和'ILasm1.MainClass.AddNumbers(int,int)'

c#真的无法区分不同的返回类型吗?我知道我不能编写只有不同返回类型的方法,但我总是认为它会知道如何处理它。

7 个答案:

答案 0 :(得分:21)

ECMA-334 C#第8.7.3节

  

方法的签名包括   方法的名称和数字,   修饰语及其形式的类型   参数。 方法的签名   不包括退货类型。

您可以使用通用方法:

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

   throw new NotSupportedException();
}

答案 1 :(得分:18)

是的,在C#中真的不可能,我知道C ++也不允许这样做,它与解释语句的方式有关:

double x = AddNumbers(1, 2);

这里的规则是赋值是右关联的,意味着右边的表达式首先被完全评估,然后才考虑赋值,在必要时应用隐式转换。

编译器无法确定哪个版本最合适。使用一些任意规则只会招来很难找到错误。

这与这个简单的陈述有关:

double y = 5 / 2;  // y = 2.0

答案 2 :(得分:18)

像其他人一样,没有C#不支持这一点。事实上,IL支持这一点的原因是因为你必须明确返回类型,就像参数一样。例如,在IL你会说

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

IL实际上没有方法重载的概念:float AddNumbers(int, int)与IL {1}}无关,就IL而言。你必须事先告诉IL编译器一切,它永远不会试图推断你的意图(比如像C#这样的高级语言)。

请注意,大多数.NET语言和C#都会返回类型重载的一个例外:转换运算符。所以

int AddNumbers(int, int)

编译为

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

因为这是一个特殊的极端情况,必须支持原始类型(例如int - &gt; bool)和引用类型转换(否则你会得到一种非常迂腐的语言),这是处理的,但不是作为方法重载的一个例子。

答案 3 :(得分:4)

问题是有一个从int到float的自动转换,所以它真的不知道你的意图。你打算调用两个int的方法并返回一个int,然后将它转换为float,或者你打算调用两个int的方法并返回一个浮点数?最好是编译错误而不是做出错误的选择,并且在应用程序中断之前不要让你知道。

答案 4 :(得分:2)

MSIL能够在同一个程序集中保存这两个方法与编译器确定调用哪个方法无关。

协变返回类型已经讨论了很多年,它们在Java中被部分允许,C ++有一个特例,而C#设计者有added some minor changes to 4.0

对于您的玩具问题,有一些简单的典型解决方案。例如,您的float AddNumbers(int, int)可能始终与(float) AddNumbers(int, int)相同,在这种情况下,不需要第二个函数。通常,该案例使用泛型来处理:<T> AddNumbers(<T> n1, <T> n2),因此您最终会得到float AddNumbers(float, float)int AddNumbers(int, int)

现实场景更有可能是您想要返回的不同表示形式,但您不想重命名该方法。在继承/覆盖用例中,您还可以解决此somewhat with generics

在少数情况下,我想按照你的意愿去做,实际上更好地命名方法更合适,因为从长远来看它更具可读性和可维护性。

这也已经讨论here

位于http://blogs.msdn.com/abhinaba/archive/2005/10/07/478221.aspx的MSIL / C#中的情况很特殊,因为它们是显式转换运算符。

答案 5 :(得分:1)

不,没有办法做到这一点。实际上,除了带有模板的C ++之外,没有语言支持它。这显然太危险了。再说一次,如果你写的话

var a = AddNumbers(1, 1);

a假设的类型是什么?

或者如果你称之为

 double a = AddNumbers(1, 1);

甚至

 AddNumbers(1, 1);

它应该调用哪个版本?

请记住,关于如何将一种类型隐式转换为另一种类型的规则非常复杂。我们来看看不编译的简单程序

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

如果您尝试编译它,编译器会给您一个错误

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

因为1.0的类型为double,编译器实际上不知道在intfloat之间选择什么类型,所以它会要求您提供一个提示。所以你可以在调用函数之前继续转换参数。

但是如果它是函数重载的返回类型,那你怎么做呢?根本没办法做到这一点。

答案 6 :(得分:-3)

如何使用指针将返回类型作为参数传递?

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

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

调用它会变成这样:

int a;
float b;
AddNumbers(1, 2, &a);
AddNumbers(1, 2, &b);