2016-11-15 5 views
3

ich eine Klasse (für die Zwecke dieser Frage vereinfacht), die einen decimal Wert wickelt und verwendet ein paar implicit operator Erklärungen für zwischen der Art und dem umhüllten Wert Umwandlung:Implizite Operatoren und ein Compilerfehler

private class DecimalWrapper 
{ 
    public decimal Value { get; private set; } 

    public DecimalWrapper(decimal value) 
    { 
     Value = value; 
    } 

    public static implicit operator decimal(DecimalWrapper a) 
    { 
     return a != null ? a.Value : default(decimal); 
    } 

    public static implicit operator DecimalWrapper(decimal value) 
    { 
     return new DecimalWrapper(value); 
    } 
} 

Die Verbräuche von implicit operator hier erlauben Dinge wie diese zu tun:

DecimalWrapper d1 = 5; // uses implicit operator DecimalWrapper 
DecimalWrapper d2 = 10; 
var result = d1 * d2; // uses implicit operator decimal 

Assert.IsTrue(result.Equals(50)); 
Assert.IsTrue(result == 50); 

nun eine zweite Klasse betrachten (auch hier vereinfacht), die einen überladenen Konstruktor hat, die einenehmen 10 oder ein DecimalWrapper:

private class Total 
{ 
    private readonly DecimalWrapper _total; 

    public Total(DecimalWrapper total) 
    { 
     _total = total; 
    } 

    public Total(decimal totalValue) 
    { 
     _total = totalValue; 
    } 
} 

ich erwarten zu können, wäre eine Instanz von Total, indem in einem ganzzahligen Wert instanziiert, die in eine Dezimalzahl bekommen umgewandelt würden:

var total = new Total(5); 

Doch diese Ergebnisse in einem Compiler-Fehler:

The call is ambiguous between the following methods or properties: 'Namespace.Total.Total(TypeTests.DecimalWrapper)' and 'Namespace.Total.Total(decimal)'

um dies zu beheben, müssen Sie die implicit operator decimal oder geben Sie entfernen, dass der Wert.210 ist in der Tat ein decimal:

var total = new Total(5m); 

Das ist alles gut und schön, aber ich sehe nicht, warum die implicit operator decimal relevant ist hier. Also, was ist los?

+0

"wird nicht einmal ausgeführt, wenn das läuft" --- warum sollte es? Sie haben eine Dezimalzahl übergeben, so dass keine Umwandlung erforderlich ist - der Konstruktor 'Total (dezimal totalValue) 'entspricht genau diesem Wert. – zerkms

+0

@zerkms - Ich habe meine Frage ein wenig geklärt –

+1

Es ist relevant, weil es 'Total (DecimalWrapper Total)' Konstruktor und eine implizite Konvertierung 'Int -> Dezimal -> DecimalWrapper'.Der Compiler (nachdem er die genaue Signaturübereinstimmung nicht gefunden hat) überprüft, welche Konstruktoren er nach der Typkonvertierung erfüllen kann, und in diesem Fall ist es beides. – zerkms

Antwort

3

Suchen Sie ein Zitat aus der Sprachspezifikation?

Die Ursache dafür hat mit der Überladungsauflösung zu tun. Wenn Sie einen int-Wert als Konstruktorparameter angeben, wird keine Überladung als "beste" betrachtet, da beide eine Konvertierung erfordern. Die Spezifikation berücksichtigt nicht zwei Konvertierungsebenen, die sich von einer Ebene unterscheiden, sodass die beiden Konstruktorüberladungen einander entsprechen.

Wie Blorgbeard noted in the comments können Sie das Problem einfach lösen, indem Sie einen der Konstruktoren loswerden. Er schlägt vor, die DecimalWrapper Überladung zu entfernen, aber da Ihr Feld vom DecimalWrapper Typ ist, würde ich die decimal Überlastung stattdessen loswerden. Wenn Sie auf diese Weise einen int Wert für den Konstruktor angeben, konvertiert der Compiler implizit in decimal und dann DecimalWrapper für Sie. Wenn Sie einen decimal Wert für den Konstruktor angeben, wird der Compiler für diesen Aufruf in DecimalWrapper konvertiert, was Ihr decimal Konstruktor sowieso getan hätte.

Natürlich wäre eine weitere Möglichkeit, das Problem zu lösen, das Hinzufügen anderer Konstruktoren zu der Klasse Total, z. eine, die eine int nimmt. Dann ist keine Konvertierung erforderlich und der int Konstruktor würde ausgewählt werden. Aber das scheint mir übertrieben zu sein.