¿Realmente es imposible usar la sobrecarga de tipo de retorno?

Hice una pequeña DLL en MSIL con dos métodos:

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

Como algunos de ustedes sabrán, MSIL le permite hacer métodos que tienen los mismos argumentos, siempre y cuando tenga diferentes tipos de tipos de devolución (lo que se denomina sobrecarga del tipo de devolución). Ahora, cuando traté de usarlo desde c #, como estaba esperando, se produjo un error:

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

El error es:

La llamada es ambigua entre los siguientes métodos o propiedades: ‘ILasm1.MainClass.AddNumbers (int, int)’ y ‘ILasm1.MainClass.AddNumbers (int, int)’

Es c # realmente incapaz de distinguir entre diferentes tipos de retorno? Sé que no puedo progtwigr métodos que tengan como única diferencia diferentes tipos de devolución, pero siempre supuse que sabría cómo manejarlo.

Como todos los demás han dicho, ningún C # no es compatible con esto. De hecho, la razón por la que IL lo admite es porque tiene que ser explícito sobre los tipos de devolución, al igual que los parámetros. Por ejemplo, en IL dirías

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

IL realmente no tiene una noción de sobrecarga de método: float AddNumbers(int, int) no tiene relación con int AddNumbers(int, int) absoluto, en lo que concierne a IL. Tienes que decirle al comstackdor de IL todo con antelación, y nunca intenta inferir tu intención (como los lenguajes de nivel superior como C # do).

Tenga en cuenta que la mayoría de los lenguajes .NET y C # hacen una excepción para la sobrecarga de tipo de retorno: operadores de conversión. Asi que

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

están comstackdos para

 public static B op_Explicit(A a); public static C op_Explicit(A a); 

Debido a que este es un caso de esquina tan específico que debe ser soportado por tipos primitivos (como int -> bool) y conversiones de tipo de referencia (de lo contrario obtendría un lenguaje muy pedante), esto se maneja, pero no como un caso de sobrecarga de método

ECMA-334 C # Sección 8.7.3

La firma de un método consiste en el nombre del método y el número, modificadores y tipos de sus parámetros formales. La firma de un método no incluye el tipo de devolución.

Puede usar un método genérico:

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

Sí, realmente no es posible en C #, sé que C ++ tampoco permite esto, tiene que ver con la forma en que se interpreta una statement:

 double x = AddNumbers(1, 2); 

La regla aquí es que la asignación es de asociación correcta, lo que significa que la expresión de la derecha se evalúa por completo primero y solo entonces se considera la asignación, aplicando conversiones implícitas cuando sea necesario.

No hay forma de que el comstackdor determine qué versión es la más adecuada. El uso de una regla arbitraria solo provocaría errores difíciles de encontrar.

Está relacionado con esta simple statement:

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

El problema es que hay una conversión automática de int a float, por lo que realmente no sabe lo que pretendía. ¿Pretendes llamar al método que toma dos ints y devuelve un int, luego convertirlo a float o tienes la intención de llamar al método que toma dos ints y devuelve un float? Es mejor tener un error de comstackción que hacer una elección incorrecta y no avisarle hasta que se rompa la aplicación.

MSIL es capaz de mantener ambos métodos en el mismo ensamblado y no tiene nada que ver con que el comstackdor determine a cuál llamar.

Los tipos de retorno covariantes se han discutido durante muchos años, están parcialmente permitidos en Java, C ++ tiene un caso especial y los diseñadores de C # han agregado algunos cambios menores a 4.0 .

Para su problema de juguete, hay soluciones típicas simples. Por ejemplo, su float AddNumbers(int, int) es probable que siempre sea el mismo que (float) AddNumbers(int, int) , en cuyo caso no hay necesidad de la segunda función. Normalmente, ese caso se maneja con generics: AddNumbers( n1, n2) , por lo que terminaría con float AddNumbers(float, float) e int AddNumbers(int, int) .

Es más probable que un escenario de la vida real sea donde tiene diferentes representaciones que desea devolver, pero no desea cambiar el nombre del método. En el caso de uso de herencia / anulación, también puede resolver esto de alguna manera con generics .

En los pocos casos en los que he querido hacer lo que deseaba, resultó mejor nombrar los métodos de manera más adecuada, ya que es más legible y mantenible a largo plazo.

Esto también fue discutido aquí .

El caso en MSIL / C # en http://blogs.msdn.com/abhinaba/archive/2005/10/07/478221.aspx es especial porque son operadores de conversión explícitos.

No, no hay forma de hacer eso. De hecho, excepto en C ++ con plantillas, ningún idioma lo admite. Esto es simplemente demasiado peligroso. Y de nuevo, ¿y si escribes

 var a = AddNumbers(1, 1); 

¿Qué tipo supongo que es?

O qué tal si lo llamas así

  double a = AddNumbers(1, 1); 

o incluso

  AddNumbers(1, 1); 

¿Qué versión debería llamar?

Recuerde, hay reglas bastante complicadas sobre cómo un tipo se puede convertir implícitamente a otro. Echemos un vistazo al progtwig simple que no comstack

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

Si intenta comstackrlo, el comstackdor le dará un error

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

porque 1.0 tiene el tipo double y el comstackdor no sabe realmente qué tipo elegir entre int y float por lo que le float que le dé una pista. Entonces puede seguir adelante y convertir un argumento antes de llamar a una función.

Pero si era tipo de retorno, esa función está sobrecargada por, ¿cómo se hace eso entonces? Simplemente no hay forma de hacerlo.

¿Qué hay de pasar el tipo de devolución como un parámetro usando punteros?

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

Llamarlo se convertiría en algo como esto:

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