2009-09-26 14 views
10

he cometido un pequeño archivo DLL en MSIL con dos métodos:¿Sobrecarga de tipo de retorno realmente imposible de usar?

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

Como algunos de ustedes sabrán, MSIL le permite realizar métodos que tienen los mismos argumentos, siempre y cuando usted tiene diferentes tipos de tipos de retorno (lo se llama sobrecarga de tipo de retorno). Ahora, cuando traté de usarlo desde C#, ya que estaba esperando un poco, se disparó un error:

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

El error es:

The call is ambiguous between the following methods or properties: 'ILasm1.MainClass.AddNumbers(int, int)' and 'ILasm1.MainClass.AddNumbers(int, int)'

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

+0

Parece que IL lo admite http://blogs.msdn.com/abhinaba/archive/2005/10/07/478221.aspx –

+11

Que lo admite, ya lo sé ... hice un ensamblado con él, como puede ver en lo que escribí arriba ... –

Respuesta

16

Como todo el mundo ha dicho, ninguna de C# no soporta esta. 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 la IL dirías

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

IL realmente no tiene una noción de sobrecarga de métodos: float AddNumbers(int, int) no tiene relación con int AddNumbers(int, int) en absoluto, en lo que se refiere a IL. Tienes que decirle al compilador 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 a la sobrecarga de tipo de retorno: operadores de conversión.Así

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

se compilan a

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

Debido a que este es un caso esquina tan específico que tiene que ser apoyado para los tipos primitivos (como int -> bool) y conversiones de tipos de referencia (de lo contrario' d obtener un lenguaje muy pedante), esto se maneja, pero no como un caso de sobrecarga de métodos.

18

Sí en realidad no es posible en C#, lo sé C++ no permite que este sea, tiene que ver con la forma en que se interpreta un comunicado:

double x = AddNumbers(1, 2); 

La regla aquí es que la asignación es correcta -asociativo, es decir, 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 compilador determine qué versión es la más adecuada. El uso de una regla arbitraria solo provocaría errores difíciles de encontrar.

Está relacionada con esta declaración simple:

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

Buen ejemplo con el doble. – RichardOD

+0

Entonces, ¿realmente no hay forma de especificar qué método desea llamar en C# cuando están sobrecargados con el tipo de devolución? – thecoop

+0

@thecoop: No, y es por eso que no se puede sobrecargar por tipo de devolución. –

4

El problema es que hay una conversión automática de int a flotar por lo que realmente no sabe lo que pretende. ¿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 compilación que hacer una elección incorrecta y no avisarle hasta que se rompa la aplicación.

+0

MSIL puede hacerlo, jajaja. –

+2

@devoured no, no puede. MSIL no adivina a cuál te refieres; usted especifica a cuál se refería. C# no tiene ningún mecanismo para especificar en este caso. –

-3

¿Qué tal pasar el tipo de devolución como 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); 
} 

Calling se convertiría en algo como esto:

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

Sí, es una buena idea, pero no es exactamente lo que pedí. –

+5

C# puede hacerlo sin punteros :) – vava

+0

Esto es cierto, pero como se mencionó anteriormente, no puede hacer lo que desea hacer. Esto es un trabajo alrededor. Resuelve el mismo problema, pero de una manera diferente. A menos que haya otros requisitos que no haya mencionado. Si los hay, escuchemoslos y veremos si podemos mejorar esta solución;) – dharga

1

No, no hay manera de hacer eso. De hecho, excepto en C++ con plantillas, ningún idioma lo admite. Esto es simplemente demasiado peligroso. Y de nuevo, lo que si se escribe

var a = AddNumbers(1, 1); 

qué tipo a supone que debe ser?

O qué si usted lo llama como

double a = AddNumbers(1, 1); 

o incluso

AddNumbers(1, 1); 

qué versión se debe llamar?

Recuerde, hay reglas bastante complicadas sobre cómo un tipo se puede convertir implícitamente a otro.Vamos a echar un vistazo a simple programa que no compila

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 compilar, compilador le dará un error

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

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

Pero si fue el tipo de retorno que la función está sobrecargada, ¿cómo se hace eso entonces? Simplemente no hay forma de hacerlo.

+0

En el primer caso, se activará una advertencia diciendo "x tipo de retorno asumido". El segundo caso, devolvería la versión flotante, el tercer caso funcionaría igual que el primero. –

+0

Bueno, el gran punto es: si MSIL puede hacerlo, ¿por qué no haría C# do? –

+1

No tengo idea de por qué MSIL hace eso. Tal vez puedas seleccionar la función que deseas. Pero la idea con advertencia es simplemente incorrecta. Imagina que obtienes dos funciones que producen efectos secundarios ligeramente diferentes. El programa funciona bien hasta que elimine una de las funciones o agregue una tercera. Y luego, todo por arte de magia deja de funcionar y te pierdes completamente tratando de descubrir qué sucedió porque tendrías errores en todo el sistema, que parecerían no estar relacionados con el último cambio. Es por eso que C++ se considera peligroso, es por eso que nadie irá con una advertencia en caso de que el compilador no pueda decidir aquí. – vava

2

MSIL ser capaz de mantener ambos métodos en el mismo conjunto no tiene nada que ver con el compilador que determina a cuál llamar.

Los tipos de retorno covariantes se han discutido durante muchos años, parcialmente se permiten en Java, C++ tiene un caso especial y los diseñadores de C# tienen added some minor changes to 4.0.

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

Un escenario de la vida real es más probable que 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 este somewhat with generics.

En los pocos casos en que he querido hacer lo que quisiera, en realidad 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 se discutió ya here.

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.

+0

Ni nadie dijo eso. –

+0

¿Podría destacar qué parte de la página enlazada "sugiere que podría venir"? –

+0

Palabras corregidas para mencionar solo los cambios 4.0. –

21

ECMA-334 C# Sección 8.7.3

The signature of a method consists of the name of the method and the number, modifiers, and types of its formal parameters. The signature of a method does not include the return type.

Se puede usar un método genérico:

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

El uso de un método genérico es una buena forma de hacerlo. – acarlon

Cuestiones relacionadas