2010-12-12 18 views
5

Ho appena incontrato questo (codice formato da dimostrare il "problema"):Il tipo dell'espressione condizionale non può essere deterministico estratto?

public ICollection<string> CreateCollection(int x) 
{ 
    ICollection<string> collection = x == 0 
            ? new List<string>() 
            : new LinkedList<string>(); 
    return collection; 
} 

Il compilatore si lamenta: CS0173

errore: Il tipo di espressione condizionale non può essere determinato, perché nessuno conversione implicita tra 'System.Collections.Generic.List 'e' System.Collections.Generic.LinkedList "si svolge.

Quali tardive trans grosso modo:

Il tipo di operatore condizionale non può essere estratto deterministico, perché non c'è alcuna conversione implicita tra List e LinkedList.

Posso capire perché il compilatore si lamenta, ma hey, andiamo. Sta cercando di suonare stupido. Vedo fatto Entrambe le espressioni non sono dello stesso tipo, ma hanno un antenato comune e come bonus il tipo del lato sinistro è quindi un antenato comune. Sono sicuro che anche il compilatore può vederlo. Potrei capire l'errore se il leftside che ha dichiarato come var.

Che cosa mi manca qui?

Edit:

sto accettando la spiegazione di James Gaunt. Forse basta chiarire. Posso leggere le specifiche del compilatore bene. Volevo capire perché. Perché qualcuno ha preso la decisione di scrivere le specifiche in questo modo. Ci deve essere una ragione dietro questo design. Secondo James, il principio di design è "senza sorprese". Così CodeInChaos Spiega quali sorprese si potrebbero incontrare se il compilatore avrebbe cercato di dedurre il tipo da antenati comuni.

+1

Provare a inserire un cast esplicito su 'ICollection ' su uno dei risultati condizionali. –

+0

So come aggirarlo, voglio solo capirlo. – EricSchaefer

+0

e se ...il compilatore creerebbe un tipo anonimo che (A) implementasse tutte le interfacce comuni (B) come un wrapper per l'oggetto selezionato? – m0sa

risposta

9

L'espressione (un B:? C) deve risolvere a un tipo. Il tipo vuole essere o il tipo di boc. Se questi sono diversi il compilatore non sa quale è in fase di compilazione.

Si può dire che shoulderstand dedurre thatthere è digitare radice comune, ma c'è sempre un tipo di radice comune (E. G. Object).

In generale, il compilatore C# non tenterà di indovinare quello che vuoi dire. Se si desidera utilizzare il tipo di root comune, eseguire il cast e c per quel tipo.

Questo tipo di logica funziona in tutto il design di C#, a volte è un po 'fastidioso, ma lontano da più spesso si ferma a fare errori.

+0

Bene con 'var' il compilatore prova a indovinare ... – EricSchaefer

+9

No, non indovina. var può essere utilizzato solo se il tipo RHS è noto. var non fa in modo che il compilatore faccia qualcosa di diverso, ma ti risparmia semplicemente la digitazione. –

+3

Questo tipo di domanda arriva regolarmente sul blog di Eric Lippert. Alla fine della giornata è un principio del design di base di C#, a volte si riferisce ad esso come 'senza sorprese'. –

2

Il lato sinistro non viene presa in considerazione affatto Quando deterministico mineraria del tipo di lato destro.

E 'solo quando il compilatore ha unabhängig minato il tipo del lato destro deterministico ha fatto controlla la compatibilità incarico con il lato sinistro.

quanto riguarda il tuo claimsoft ha Entrambi i tipi "hanno un antenato comune": si tratterebbe ICollection, IEnumerable, ICollection<T>, IEnumerable<T> o Object? Che euristica shoulderstand l'uso compilatore di miniera senza ambiguità deterministico Quale tipo si vuoi? Il compilatore ti sta semplicemente chiedendo di specificare, piuttosto che cercare di indovinare la tua intenzione.

+0

Tuttavia, entrambe le espressioni sul lato destro hanno un antenato comune (tipo saggio). – EricSchaefer

+0

hanno molti antenati comuni, il che fa sì che il compilatore lanci un errore ... ti aspetti che funzioni anche così? (var obj = true? 1: "0"), quale tipo sarebbe obj ora? secondo la tua logica sarebbe di tipo Object, ma questo è piuttosto inverosimile. –

+0

Beh, almeno il compilatore potrebbe provare a essere intelligente. Come un cliente si lamenta sempre: "Perché devo farlo, lascia che sia il computer a farlo ..." ;-) – EricSchaefer

2

Semplicemente la definizione di ?: richiede tipi uguali.
Si potrebbe ovviamente usare

? (ICollection<string>) new List<string>() 
: (ICollection<string>) new LinkedList<string>(); 

O semplicemente usare un if/else.

Secondo il C# riferimento, § 14.13,

[Data] Un'espressione condizionale della forma b ? x : y

  • Se X e Y sono dello stesso tipo, allora questo è il tipo di condizionale espressione.
  • Altrimenti, se una conversione implicita (§13.1) esiste da X a Y, ma non da Y a X, allora Y è il tipo di l'espressione condizionale.
  • Altrimenti, se esiste una conversione implicita (§13.1) da Y a X, ma non da X a Y, X è il tipo di espressione condizionale.
  • In caso contrario, non è possibile determinare alcun tipo di espressione e si verifica un errore in fase di compilazione.

Nel tuo caso, sia X che Y hanno una conversione in Z, ma questo non aiuta. È un principio generale nella lingua che il compilatore non osserva nemmeno la variabile di destinazione quando applica le regole. Esempio semplice: double a = 7/2; // a becomes 3.0

Così, dopo aver letto questo ho cosa basterebbe lanciare solo dei risultati per ICollection<string>. Non l'ho provato.

+0

Lo capisco. Voglio sapere perché. – EricSchaefer

+0

@EricSch, OK, ho citato le regole. –

+0

Posso leggere le regole. Ancora una volta: perché? È una decisione di design che è stata presa per una ragione. Volevo sapere il motivo ... – EricSchaefer

4

A causa delle interfacce possono avere più antenati comuni diversi.

Si potrebbe aggiungere un requisito che converte solo se l'antenato non è ambiguo. Ma poi aggiungendo interfacce aggiuntive, le implementazioni di classe diventano improvvisamente un cambiamento decisivo. E questo potrebbe non essere desiderabile.

Ad esempio, si supponga di implementare questi tipi ISerializeable. Questo non dovrebbe cambiare il comportamento del tuo codice, ma se tu avessi supportato quel casting nell'interfaccia comune lo farebbe.

edit: ci pensò un po 'più su di esso e ho notato che questa funzione ha già esattamente lo stesso problema:

T MyFunc<T>(T left,T right) 

e questo codice non viene compilato:

perché può' t decidere quale tipo usare come parametro di tipo T. Quindi il comportamento dell'operatore?: È coerente con la risoluzione di sovraccarico.

+0

Ora questo ha senso (specialmente la parte riguardante l'aggiunta successiva di nuovi antenati). – EricSchaefer

Problemi correlati