2009-09-26 11 views
10

Ich habe eine kleine DLL in MSIL mit zwei Methoden:Wirklich unmöglich, Rückgabetyp Überladung zu verwenden?

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

Wie einige von euch vielleicht wissen, MSIL können Sie Methoden machen, die die gleichen Argumente wie lange haben, wie Sie verschiedene Arten von Rückgabetypen haben (was heißt Rücklauftyp Überladung). Nun, wenn ich versuchte, es von C# zu verwenden, da ich irgendwie erwartete, feuerte er einen Fehler:

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

Der Fehler ist:

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

Ist C# wirklich unfähig, zwischen unterschiedlichen Rückgabetypen unterscheiden ? Ich weiß, dass ich keine Methoden programmieren kann, die nur unterschiedliche Rückgabetypen haben, aber ich nahm immer an, dass sie wissen, wie man damit umgeht.

+0

Scheint, dass IL es unterstützt http://blogs.msdn.com/abhinaba/archive/2005/10/07/478221.aspx –

+11

Dass es es unterstützt, weiß ich bereits .. Ich habe eine Versammlung damit gemacht, wie Sie können siehe in dem, was ich oben geschrieben habe .. –

Antwort

16

Wie jeder, ist kein C# dies nicht unterstützen. Der Grund dafür, dass IL dies unterstützt, ist, dass Sie genau wie die Parameter explizit auf die Rückgabetypen hinweisen müssen. Zum Beispiel in IL würden Sie

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

IL sagt nicht wirklich ein Begriff der Methode Überlastung haben: float AddNumbers(int, int) hat keine Beziehung zu int AddNumbers(int, int) auch immer, so weit wie IL betrifft. Sie müssen dem IL-Compiler alles im Voraus mitteilen, und es versucht nie, Ihre Absicht abzuleiten (wie höhere Programmiersprachen wie C#).

Beachten Sie, dass die meisten .NET-Sprachen und C# eine Ausnahme machen, um das Überladen von Typen zurückzugeben: Konvertierungsoperatoren.So

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

zusammengestellt zu

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

Da es sich um eine solche bestimmte Ecke Fall, die für primitive Typen unterstützt werden muss (wie int -> Bool) und Referenztypkonvertierungen (sonst Sie‘ d eine sehr pedantische Sprache bekommen), dies wird behandelt, aber nicht als eine Methode der Überladung von Methoden.

18

Ja, es ist wirklich nicht möglich, in C#, weiß ich C++ erlaubt es nicht, diese entweder, es mit der Art und Weise zu tun hat, eine Aussage interpretiert wird:

double x = AddNumbers(1, 2); 

Die Regel ist, dass die Zuordnung richtig ist -assoziativ, dh der Ausdruck auf der rechten Seite wird zuerst vollständig ausgewertet und erst dann wird die Zuweisung berücksichtigt, wobei implizite Konvertierungen bei Bedarf angewendet werden.

Es gibt keine Möglichkeit für den Compiler zu bestimmen, welche Version am besten geeignet ist. Die Verwendung einer beliebigen Regel würde nur schwer zu Fehlern führen.

Es ist auf diese einfache Aussage bezogen werden:

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

Schönes Beispiel mit dem Doppel. – RichardOD

+0

Also gibt es eigentlich keine Möglichkeit zu spezifizieren, welche Methode Sie in C# aufrufen möchten, wenn sie mit Rückgabetyp überladen werden? – thecoop

+0

@thecoop: Nein, und deshalb können Sie nicht nach Rückgabetyp überladen. –

4

Das Problem ist, dass es eine automatische Konvertierung von int ist es nicht so wirklich zu schweben, was Sie gedacht. Haben Sie vor, die Methode aufzurufen, die zwei Ints verwendet und ein int zurückgibt, dann wandeln Sie es in float um oder wollten Sie die Methode aufrufen, die zwei Ints benötigt und einen Gleitkommawert zurückgibt? Es ist besser, einen Compilerfehler zu haben, als die falsche Wahl zu treffen und Sie nicht zu informieren, bis Ihre Anwendung bricht.

+0

MSIL kann es tun, lol. –

+2

@devoured nein, kann es nicht. MSIL rät nicht, welchen du gemeint hast; Sie geben an, welche Sie gemeint haben. C# hat in diesem Fall keinen Mechanismus zur Angabe. –

-3

Wie wird der Rückgabetyp als Parameter mithilfe von Zeigern übergeben?

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

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

aufrufen würde es so etwas wie dieses werden:

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

Ja, das ist eine nette Idee, aber nicht wirklich das, wonach ich gefragt habe. –

+5

C# kann das ohne Zeiger tun :) – vava

+0

Das ist wahr, aber wie oben erwähnt, können Sie nicht tun, was Sie tun möchten. Das ist eine Arbeit. Es löst das gleiche Problem, aber auf andere Weise. Es sei denn, es gibt andere Anforderungen, die Sie nicht erwähnt haben. Wenn ja, hören wir sie und wir werden sehen, ob wir diese Lösung verbessern können;) – dharga

1

Nein, es gibt keine Möglichkeit, das zu tun. Abgesehen von C++ mit Vorlagen wird keine Sprache unterstützt. Das ist einfach zu gefährlich. Und wieder, was passiert, wenn Sie schreiben

var a = AddNumbers(1, 1); 

welche Art a sein annehmen?

Oder was ist, wenn Sie es wie

double a = AddNumbers(1, 1); 

nennen oder sogar

AddNumbers(1, 1); 

welche Version sollte es nennen?

Denken Sie daran, es gibt recht komplizierte Regeln darüber, wie ein Typ implizit in einen anderen konvertiert werden kann.Lassen Sie uns einen Blick auf einfaches Programm nehmen, die nicht

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

nicht kompilieren Wenn Sie es zu kompilieren versuchen, Compiler erhalten Sie einen Fehler geben

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

weil 1.0 double und Compiler wirklich tut, geben Sie nicht wissen, welcher Typ zwischen int und float zu wählen, so dass es Sie fragt, gibt es einen Hinweis. Sie können also einfach ein Argument vor dem Aufruf einer Funktion konvertieren.

Aber wenn es Rückgabetyp war, dass die Funktion überlastet wird, wie machst du das dann? Es gibt einfach keinen Weg es zu tun.

+0

Im ersten Fall würde eine Warnung mit der Angabe "x Rückgabetyp angenommen" ausgegeben. Der zweite Fall würde die Float-Version zurückgeben, der dritte Fall würde genauso funktionieren wie der erste. –

+0

Nun, der große Punkt ist: Wenn MSIL es kann, warum würde C# nicht tun? –

+1

Hab keine Ahnung, warum MSIL das macht. Vielleicht können Sie auswählen, welche Funktion Sie möchten. Aber Idee mit Warnung einfach falsch. Stellen Sie sich vor, Sie erhalten zwei Funktionen, die leicht unterschiedliche Nebenwirkungen haben. Programm funktioniert gut, bis Sie eine der Funktionen entfernen oder eine dritte hinzufügen. Und dann hört alles auf magische Weise auf zu arbeiten, und Sie haben völlig verloren, wenn Sie herausfinden wollten, was passiert ist, weil Sie im ganzen System Fehler haben würden, die völlig unabhängig von der letzten Änderung zu sein scheinen. Das ist der Grund, warum C++ als gefährlich angesehen wird, deshalb wird niemand jemals mit Warnung gehen, falls der Compiler hier nicht entscheiden kann. – vava

2

MSIL in der Lage, beide Methoden in der gleichen Assembly zu halten, hat nichts mit dem Compiler zu tun, der bestimmt, welcher Aufruf aufgerufen wird.

Kovariante Rückgabetypen werden seit vielen Jahren diskutiert, sie sind teilweise in Java erlaubt, C++ hat einen Sonderfall, und die C# -Designer haben added some minor changes to 4.0.

Für Ihr Spielzeugproblem gibt es einfache typische Lösungen. Zum Beispiel ist Ihre float AddNumbers(int, int) wahrscheinlich immer die gleiche wie (float) AddNumbers(int, int), in diesem Fall gibt es keine Notwendigkeit für die zweite Funktion. In der Regel wird dieser Fall mit Generics behandelt: <T> AddNumbers(<T> n1, <T> n2), also würden Sie mit float AddNumbers(float, float) und int AddNumbers(int, int) enden.

Ein reales Szenario ist wahrscheinlich, wo Sie verschiedene Darstellungen haben, die Sie zurückgeben möchten, aber Sie möchten die Methode nicht umbenennen. Im Use Case Vererbung/Überschreibung können Sie auch somewhat with generics lösen.

In den wenigen Fällen, wo ich wollte, wie Sie wollen, ist es tatsächlich besser, die Methoden besser zu benennen, da es auf lange Sicht besser lesbar und wartbar ist.

Dies wurde auch bereits diskutiert here.

Der Fall in MSIL/C# um http://blogs.msdn.com/abhinaba/archive/2005/10/07/478221.aspx ist speziell, weil sie explizite Konvertierungsoperatoren sind.

+0

Noch hat das jemand gesagt. –

+0

Können Sie hervorheben, welches Bit der verlinkten Seite "Hinweise darauf gibt, dass es kommen könnte"? –

+0

Korrigierte Formulierung, um nur die 4.0 Änderungen zu erwähnen. –

21

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

Sie eine generische Methode verwenden:

anderes gesagt hat
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

Die Verwendung einer generischen Methode ist eine gute Möglichkeit, dies zu tun. – acarlon

Verwandte Themen