Frage

Fehlschlägt:

object o = ((1==2) ? 1 : "test");

Gelingt es:

object o;
if (1 == 2)
{
    o = 1;
}
else
{
    o = "test";
}

Der Fehler in der ersten Anweisung lautet:

Die Art der bedingten Expression kann nicht bestimmt werden, da keine implizite Umwandlung zwischen 'int' und 'String' vorliegt.

Warum muss ich diese Werte jedoch einem Variablen des Typs des Typs zuweisen?

Bearbeiten: Das obige Beispiel ist trivial, ja, aber es gibt Beispiele, bei denen dies sehr hilfreich wäre:

int? subscriptionID; // comes in as a parameter

EntityParameter p1 = new EntityParameter("SubscriptionID", DbType.Int32)
{
    Value = ((subscriptionID == null) ? DBNull.Value : subscriptionID),
}
War es hilfreich?

Lösung

verwenden:

object o = ((1==2) ? (object)1 : "test");

Das Problem ist, dass der Rückgabetyp des bedingten Operators nicht unbedingt bestimmt werden kann. Das heißt, zwischen Int und String gibt es keine beste Wahl. Der Compiler verwendet immer den Typ des wahren Ausdrucks und wirft bei Bedarf den falschen Ausdruck implizit.

Bearbeiten:In Ihrem zweiten Beispiel:

int? subscriptionID; // comes in as a parameter

EntityParameter p1 = new EntityParameter("SubscriptionID", DbType.Int32)
{
    Value = subscriptionID.HasValue ? (object)subscriptionID : DBNull.Value,
}

PS:
Das wird nicht als "ternärer Operator" bezeichnet. Es ist Ein ternärer Operator, aber als "bedingter Bediener" bezeichnet.

Andere Tipps

Obwohl die anderen Antworten sind Korrekt, In dem Sinne, dass sie wahre und relevante Aussagen machen, gibt es hier einige subtile Punkte des Sprachdesigns, die noch nicht zum Ausdruck gebracht wurden. Viele verschiedene Faktoren tragen zum aktuellen Design des bedingten Bedieners bei.

Erstens ist es wünschenswert für so viele Ausdrücke wie möglich, einen eindeutigen Typ zu haben, der ausschließlich aus dem Inhalt des Ausdrucks bestimmt werden kann. Dies ist aus mehreren Gründen wünschenswert. Zum Beispiel: Es erleichtert den Bau eines IntelliSense -Motors erheblich. Du tippst x.M(some-expression. und IntelliSense muss in der Lage sein, analysieren zu können Ein-Ausdruck, bestimmen Sie seinen Typ und erzeugen Sie einen Dropdown, bevor IntelliSense weiß, auf welche Methode XM bezieht. IntelliSense kann nicht wissen, was XM sicher bezieht, ob M überlastet wird, bis es alle Argumente sieht, aber Sie haben noch nicht einmal das erste Argument eingegeben.

Zweitens bevorzugen wir die Typinformationen, die "von innen nach außen" fließen, da genau das Szenario, das ich gerade erwähnt habe,: Überlastauflösung. Folgendes berücksichtigen:

void M(object x) {}
void M(int x) {}
void M(string x) {}
...
M(b ? 1 : "hello");

Was soll das tun? Sollte es die Objektüberladung aufrufen? Sollte es manchmal die String -Überladung aufrufen und manchmal die int -Überladung aufrufen? Was wäre, wenn Sie eine andere Überladung hätten, sagen Sie? M(IComparable x) - Wann wählst du es aus?

Die Dinge werden sehr kompliziert, wenn die Informationen "in beide Richtungen fließen". Sagen Sie "Ich habe dieses Ding einer Variablen des Typs des Typs zuweisen. Daher sollte der Compiler wissen, dass es in Ordnung ist, Objekt als Typ zu wählen" nicht waschen. Es ist oft der Fall, dass Wir kennen die Art der Variablen, die Sie zuweisen, nicht, da wir gerade versuchen, herauszufinden. Überlastungsauflösung ist genau der Prozess des Ausarbeitens der Arten der Parameter, die die Variablen sind, denen Sie die Argumente zuweisen, aus den Argumenten. Wenn die Argumenten von den Arten abhängen, denen sie zugewiesen werden, haben wir eine Zirkularität in unserer Argumentation.

Typinformationen fließen "in beide Richtungen" für Lambda -Ausdrücke. Die Implementierung dieser effizienten Zeit habe mich den größten Teil eines Jahres geführt. Ich habe eine lange Reihe von Artikeln geschrieben, die einige der Schwierigkeiten beim Entwerfen und Implementieren eines Compilers beschreiben, der eine Analyse durchführen kann, bei der Typinformationen in komplexe Ausdrücke basieren, basierend auf dem Kontext, in dem der Ausdruck möglicherweise verwendet wird. Teil eins ist da:

http://blogs.msdn.com/ericlippert/archive/2007/01/10/lambda-expressions-vs--anonymous-method-part-one.aspx

Sie könnten sagen: "Nun, ok, ich verstehe, warum die Tatsache, dass ich dem Objekt zuweisen kann des Expressionsobjekts, da sowohl int als auch String zu Objekt konvertierbar sind? " Dies bringt mich zu meinem dritten Punkt:

Drittens ist eines der subtilen, aber konsequent angewendeten Designprinzipien von C# "keine Typen nach Magie produzieren". Wenn wir eine Liste von Ausdrücken gegeben haben, aus denen wir einen Typ bestimmen müssen, müssen wir bestimmen, Der Typ, den wir bestimmen, befindet sich immer irgendwo in der Liste. Wir zaubern nie einen neuen Typ und wählen ihn für Sie aus. Der Typ, den Sie erhalten, ist immer einer, aus dem Sie uns zur Auswahl gegeben haben. Wenn Sie sagen, dass Sie den besten Typ in einer Reihe von Typen finden, finden wir den besten Typ in dieser Reihe von Typen. Im Set {int, String} gibt es keinen besten gemeinsamen Typ, so wie es in "Tier, Schildkröte, Säugetier, Wallaby" ist. Diese Entwurfsentscheidung gilt für den bedingten Operator, um Inferenz -Vereinigungsszenarien zu typern, um implizit getippte Array -Typen zu erreichen usw.

Der Grund für diese Designentscheidung ist, dass es den gewöhnlichen Menschen leichter erleichtert, herauszufinden, was der Compiler in einer bestimmten Situation tun wird, in der ein bester Typ bestimmt werden muss. Wenn Sie wissen, dass ein Typ, der genau dort ist und Sie ins Gesicht starrt, ausgewählt wird, ist es viel einfacher, herauszufinden, was passieren wird.

Es vermeidet auch, dass wir viele komplexe Regeln für die beste gemeinsame Art von Typen ausarbeiten müssen, wenn es Konflikte gibt. Angenommen, Sie haben Typen {foo, bar}, in denen beide Klassen Iblah implementieren, und beide Klassen erben von BAZ. Welches ist der beste gemeinsame Typ, Iblah, der sich sowohl implementieren als auch baz, die sich beide erstrecken? Wir wollen diese Frage nicht beantworten müssen. Wir wollen es ganz vermeiden.

Schließlich stelle ich fest, dass der C# Compiler in einigen obskuren Fällen tatsächlich die Bestimmung der Typen erhält. Mein erster Artikel darüber ist hier:

http://blogs.msdn.com/ericlippert/archive/2006/05/24/type-inference-woes-tone.aspx

Es ist angemessen, dass der Compiler es tatsächlich richtig macht und die Spezifikation falsch ist. Das Implementierungsdesign ist meiner Meinung nach besser als das spezifische Design.

Wie auch immer, das sind nur ein paar Gründe für die Gestaltung dieses speziellen Aspekts des ternären Operators. Hier gibt es hier noch andere Feinheiten, wie der CLR -Verifizierer feststellt, ob ein bestimmter Satz von Verzweigungspfaden garantiert den richtigen Typ auf dem Stapel auf allen möglichen Pfaden hinterlässt. Das im Detail detaillierte Diskussion würde mich ziemlich weit weg bringen.

Warum ist Feature X auf diese Weise oft eine sehr schwierige Frage zu beantworten? Es ist viel einfacher, das tatsächliche Verhalten zu beantworten.

Meine gebildete Vermutung, warum. Der bedingte Bediener darf prägnant und knapp einen booleschen Ausdruck verwenden, um zwischen 2 verwandten Werten zu wählen. Sie müssen verwandt sein, weil sie an einem einzigen Ort verwendet werden. Wenn der Benutzer stattdessen 2 nicht verwandte Werte auswählt, hatte der Code ein subtiler Tippfehler / Fehler im Code, und der Compiler ist es besser, sie darauf zu alarmieren, anstatt implizit auf Objekt zu werfen. Das kann etwas sein, das sie nicht erwartet haben.

"int" ist ein primitiver Typ, kein Objekt, während "String" eher als "primitives Objekt" betrachtet wird. Wenn Sie so etwas wie "Objekt o = 1" tun, boxen Sie tatsächlich das "int" zu einem "int32". Hier ist ein Link zu einem Artikel über Boxen:

http://msdn.microsoft.com/en-us/magazine/cc301569.aspx

Im Allgemeinen sollte das Boxen aufgrund von Leistungsverlusten vermieden werden, die schwer zu verfolgen sind.

Wenn Sie einen ternären Ausdruck verwenden, betrachtet der Compiler die Zuordnungsvariable überhaupt nicht, um festzustellen, wie der endgültige Typ ist. Um Ihre ursprüngliche Aussage in das zu unterteilen, was der Compiler tut:

Aussage: Objekt o = ((1 == 2)? 1: "Test");

Compiler:

  1. Was sind die Arten von "1" und "Test" in '((1 == 2)? 1: "Test")'? Stimmen sie zusammen?
  2. Stimmt der endgültige Typ aus #1 mit dem Zuordnungsoperatortyp für 'Objekt O' überein?

Da der Compiler Nr. 2 erst bewertet, bis Nr. 1 fertig ist, schlägt er fehl.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top