2010-11-03 11 views
5

assumere due classi, entrambi discendenti della stessa superclasse, in questo modo:L'operatore condizionale viene confuso, ma perché?

class MySuperClass{} 
class A : MySuperClass{} 
class B : MySuperClass{} 

Allora questo incarico non passerà il compilatore:

MySuperClass p = myCondition ? new A() : new B(); 

Il compilatore si lamenta che A e B non sono compatibili (Il tipo di espressione condizionale non può essere determinato perché non esiste una conversione implicita tra "A" e "B" [CS0173]). Ma sono entrambi di tipo MySuperClass, quindi secondo me dovrebbe funzionare. Non che sia un grosso problema; un semplice cast è tutto ciò che serve per illuminare il compilatore. Ma sicuramente è un ostacolo nel compilatore C#? Non sei d'accordo?

+0

Questa è una domanda frequente. Vedi ad esempio http://stackoverflow.com/questions/2215745 o http://stackoverflow.com/questions/3150086 o http://stackoverflow.com/questions/858080. Vedi anche i miei articoli sull'argomento: http://blogs.msdn.com/b/ericlippert/archive/tags/conditional+operator/ –

risposta

10

I risultati del condizionale devono essere dello stesso tipo. Non sono.

Da MSDN, (:? Operator):

sia il tipo di first_expression e second_expression deve essere uguale, o una conversione implicita deve esistere da un tipo all'altro.

Dal A e B non sono dello stesso tipo e voi non sembrano aver definito una conversione implicita, il compilatore si lamenta.

+3

Sì, prova ad aggiungere un cast: 'p = myCondition? (MySuperClass) new A(): (MySuperClass) new B(); ' – anthares

+1

@anthares - Hai davvero bisogno di scriverne uno e il compilatore ne deduce il resto -> p = myCondition? (MySuperClass) new A(): new B() –

+0

Certo che lo sono. Sono entrambi MySuperClass. A proposito; questo funziona: MySuperClass p = myCondition? (MySuperClass) new A(): new B(); Quindi è sufficiente lanciare uno dei due candidati ... Come ho detto; *Non un grande affare*. Ma ancora una curiosità, imho. – BaBu

3

Il compilatore non tenta di cercare un antenato comune, quindi è necessario un cast esplicito per mostrare quale antenato si desidera trattare come; Nel tuo caso:

MySuperClass p = myCondition ? (MySuperClass)(new A()) : (MySuperClass)(new B()); 

Ciò significa che l'operatore condizionale ha due lati returnin gthe stesso tipo, che soddisfa il compilatore.

+0

Hai una parentesi ridondante attorno a 'nuovo A()' e 'nuovo B()', il che mi fa sembrare brutto nei miei occhi. –

4

Dai un'occhiata a questo blog per alcuni articoli interessanti sul perché il compilatore C# non fa/non fa cose che ti sono "ovvie". Il blog è scritto da Eric Lippert, uno degli sviluppatori del compilatore C# .

+0

+1: questo è uno dei miei blog preferiti, –

+0

+1 Secondo la dichiarazione, trovo questo blog molto utile! – pstrjds

+3

Se hai intenzione di collegarti al suo blog, almeno metti nello sforzo di trovare un articolo pertinente. In questo caso, gli articoli di Eric su questa domanda si troveranno su http://blogs.msdn.com/b/ericlippert/archive/2010/05/27/cast-operators-do-not-obey-the-distributive-law .aspx e http://blogs.msdn.com/b/ericlippert/archive/2006/05/24/type-inference-woes-part-one.aspx tra gli altri. – Brian

1

Rowland Shaw lo ha riassunto abbastanza bene, ma per capire perché un antenato comune non è usato in modo implicito, considera cosa succederebbe se entrambe le classi implementassero anche un'interfaccia particolare. Il compilatore non sarebbe in grado di capire quale usare per il tipo di operatore condizionale, e quindi sarebbe costretto a usare object invece.

4

sguardo alla sezione 7.14 delle specifiche del linguaggio

Il secondo e terzo operandi, x ed y , da: comando del tipo dell'espressione condizionale.

· Se x è di tipo X e y ha tipo Y poi

o Se una conversione implicita (§6.1) esiste da X a Y, ma non da Y a X, allora Y è il tipo dell'espressione condizionale .

o Se una conversione implicita (§6.1) esiste da Y a X, ma non da X a Y, quindi X è il tipo di espressione condizionale .

o Altrimenti, nessun tipo di espressione può essere determinato , e si verifica un errore di compilazione .

· Se solo una di x ed y ha un tipo , ed entrambi x ed y, di areimplicitly convertibile che tipo, allora questo è il tipo di espressione condizionale.

· Altrimenti, non è possibile determinare alcun tipo di espressione e si verifica un errore in fase di compilazione.

In sostanza, gli operandi devono essere convertibile uno all'altro, non reciprocamente convertibili qualche altro tipo.

Ecco perché è necessario eseguire un cast esplicito nell'esempio o in casi come nullables (int? foo = isBar ? 42 : (int?)null). Il tipo di dichiarazione non ha alcun impatto sulla valutazione, il compilatore deve capirlo dall'espressione stessa.

3

L'operatore condizionale (come qualsiasi altro operatore) deve definire il tipo rappresentato dalla sua espressione. Nel caso dell'operatore condizionale, ha un processo in due fasi:

  1. Sono gli operandi dello stesso tipo? Se è così, questo è il tipo di espressione.
  2. Esiste una conversione implicita da uno dei tipi di operandi all'altro (ma non in entrambe le direzioni)? Se è così, allora "altro" è il tipo di espressione.

Non c'è ricerca di ascendenza, come implementazione che potrebbe portare a una scivolosa inclinazione di ambiguità in ciò che, come sviluppatore, potrebbe specificare in quell'espressione. Tutto dovrebbe portare a object? Che dire dei tipi di valore, che sarebbero quindi implicitamente racchiusi? Che dire delle interfacce? Se c'è più di un'interfaccia comune tra i due tipi, quale dovrebbe essere scelto?

Nel tuo caso, come hai scoperto, devi upcastare uno degli operandi al tipo genitore. Una volta che lo fai, regola 2) è soddisfatto (c'è sempre una conversione implicita che va da un tipo più specifico a un tipo meno specifico).

Nota che è sufficiente applicare il cast a uno degli operandi, non entrambi.

Problemi correlati