2009-09-26 9 views
10

ho fatto un piccolo DLL in MSIL con due metodi:Davvero impossibile utilizzare il sovraccarico del tipo di ritorno?

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

Come alcuni di voi sapranno, MSIL consente di effettuare metodi che hanno gli stessi argomenti fino a quando si dispone di diversi tipi di tipi di ritorno (quello è chiamato sovraccarico del tipo di ritorno). Ora, quando ho provato ad usarlo da C#, come mi era un po 'aspettavo, è sparato un errore:

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

L'errore è:

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

è C# in realtà incapaci di distinguere tra diversi tipi di ritorno ? So che non posso programmare metodi che hanno come unica differenza i diversi tipi di ritorno, ma ho sempre pensato che sapesse come gestirlo.

+0

Sembra che IL lo supporti http://blogs.msdn.com/abhinaba/archive/2005/10/07/478221.aspx –

+11

Che lo supporti lo so già .. Ho fatto un assemblaggio con esso, come puoi vedi in cosa ho scritto sopra .. –

risposta

16

Come è già stato detto, non C# non supporta questo. In effetti, la ragione per cui IL supporta questo, è perché devi essere esplicito sui tipi di ritorno, proprio come i parametri. Per esempio, in IL devi dire

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

IL realtà non hanno una nozione di overloading dei metodi: float AddNumbers(int, int) ha alcuna relazione con int AddNumbers(int, int) di sorta, per quanto IL è interessato. Devi dire al compilatore IL tutto in anticipo, e non cerca mai di dedurti l'intento (come i linguaggi di alto livello come C# do).

Si noti che la maggior parte dei linguaggi .NET e C# fanno un'eccezione per restituire il sovraccarico del tipo: operatori di conversione.Così

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

sono compilati a

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

Poiché si tratta di un caso del genere angolo specifico che deve essere sostenuta per i tipi primitivi (come int -> bool) e conversioni di tipo di riferimento (altrimenti' d avere un linguaggio molto pedante), questo è gestito, ma non come un caso di sovraccarico del metodo.

18

Sì, non è davvero possibile in C#, lo so C++ non consente questa operazione, ha a che fare con il modo in cui una dichiarazione viene interpretata:

double x = AddNumbers(1, 2); 

La regola qui è che l'assegnazione è giusto -associativo, il che significa che l'espressione sulla destra è completamente valutata per prima e solo allora l'incarico è considerato, applicando le conversioni implicite ove necessario.

Non è possibile per il compilatore determinare quale versione sia più appropriata. L'utilizzo di alcune regole arbitrarie non farebbe altro che invitare a trovare errori.

Esso è legato a questa semplice affermazione:

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

Bel esempio con il doppio. – RichardOD

+0

Quindi non c'è davvero modo di specificare quale metodo si desidera chiamare in C# quando sono sovraccaricati usando il tipo restituito? – thecoop

+0

@thecoop: No, ed è per questo che non è possibile sovraccaricare per tipo di ritorno. –

4

Il problema è che c'è una conversione automatica da int a stare a galla in modo che davvero non sa quello che intendeva. Hai intenzione di chiamare il metodo che richiede due int e restituisce un int, quindi convertirlo in float o hai intenzione di chiamare il metodo che richiede due interi e restituisce un float? È meglio avere un errore del compilatore che fare la scelta sbagliata e non farti sapere fino a quando la tua applicazione non si rompe.

+0

MSIL può farlo, lol. –

+2

@devoured no, non può. MSIL non indovina quale intendi; specifichi quale intendi. C# non ha alcun meccanismo per specificare in questo caso. –

-3

Come passare il tipo restituito come parametro utilizzando i puntatori?

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 sarebbe diventato qualcosa di simile:

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

Sì, è una bella idea, ma non proprio quella che ho chiesto. –

+5

C# può farlo senza puntatori :) – vava

+0

Questo è vero, ma come detto sopra, non puoi fare ciò che vuoi fare. Questo è un lavoro in giro. Risolve lo stesso problema, ma in un modo diverso. A meno che non ci siano altri requisiti che non hai menzionato. Se ci sono, sentiamoli e vedremo se possiamo migliorare questa soluzione;) – dharga

1

No, non c'è modo di farlo. Infatti, tranne in C++ con i modelli, nessuna lingua lo supporta. Questo è semplicemente troppo pericoloso. E ancora, che cosa se si scrive

var a = AddNumbers(1, 1); 

che tipo a supponiamo di essere?

O che cosa se lo si chiama come

double a = AddNumbers(1, 1); 

o anche

AddNumbers(1, 1); 

quale versione dovrebbe chiamare?

Ricordate, ci sono regole piuttosto complicate su come un tipo può essere convertito implicitamente in un altro.Diamo uno sguardo al semplice programma che non viene compilato

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

Se si tenta di compilarlo, compilatore vi darà un errore

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

perché 1.0 è di tipo double e compilatore in realtà non lo fa sapere che tipo di scegliere tra int e float quindi ti chiede di dargli un suggerimento. Quindi puoi semplicemente andare avanti e convertire un argomento prima di chiamare una funzione.

Ma se è stato restituito digitare la funzione è sovraccaricata, come si fa a farlo allora? Semplicemente non c'è modo di farlo.

+0

Nel primo caso, emetterebbe un avviso con "x return type assunto". Secondo caso, restituirebbe la versione float, il terzo caso funzionerebbe proprio come il primo. –

+0

Bene, il punto principale è: se MSIL può farlo, perché non farebbe C#? –

+1

Non ho idea del perché MSIL lo faccia. Forse puoi selezionare quale funzione desideri. Ma l'idea con avvertimento è semplicemente sbagliata. Immagina di avere due funzioni che producono effetti collaterali leggermente diversi. Il programma funziona bene fino a quando non rimuovi una delle funzioni o ne aggiungi una terza. E poi tutto magicamente smette di funzionare e hai perso completamente il tentativo di scoprire cosa è successo perché avresti avuto problemi su tutto il sistema, che sembrerebbe del tutto estraneo all'ultima modifica. Questo è il motivo per cui C++ è considerato pericoloso, motivo per cui nessuno andrà mai in guardia nel caso in cui il compilatore non possa decidere qui. – vava

2

MSIL è in grado di mantenere entrambi i metodi nello stesso assembly non ha nulla a che fare con il compilatore che determina quale chiamare.

I tipi di restituzione covariana sono stati discussi per molti anni, sono parzialmente consentiti in Java, il C++ ha un caso speciale ei progettisti C# hanno added some minor changes to 4.0.

Per il tuo problema di giocattoli, ci sono semplici soluzioni tipiche. Ad esempio, è probabile che il tuo float AddNumbers(int, int) sia sempre lo stesso di (float) AddNumbers(int, int), nel qual caso non è necessario per la seconda funzione. In genere quel caso è gestito con generici: <T> AddNumbers(<T> n1, <T> n2), quindi si finirebbe con float AddNumbers(float, float) e int AddNumbers(int, int).

Uno scenario reale è più probabile che si tratti di rappresentazioni diverse che si desidera restituire ma non si desidera rinominare il metodo. Nel caso di utilizzo dell'ereditarietà/esclusione, è anche possibile risolvere questo somewhat with generics.

In alcuni casi, dove ho voluto fare come si desidera, in realtà è meglio definire i metodi in modo più appropriato poiché è più leggibile e mantenibile a lungo termine.

Anche questo è stato discusso già here.

Il caso in MSIL/C# a http://blogs.msdn.com/abhinaba/archive/2005/10/07/478221.aspx è speciale perché sono operatori di conversione espliciti.

+0

Neanche qualcuno lo ha detto. –

+0

Potresti evidenziare quale bit della pagina collegata "suggerisce che potrebbe venire"? –

+0

Formulazione corretta per menzionare solo le modifiche 4.0. –

21

ECMA-334 C# Sezione 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.

Si potrebbe utilizzare un metodo generico:

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

L'uso di un metodo generico è un buon modo per farlo. – acarlon

Problemi correlati