2010-05-10 14 views
8

Ho una struct come questo, con una conversione esplicita a stare a galla:Perché ho bisogno di una conversione intermedia per passare da struct a decimal, ma non struct to int?

struct TwFix32 
{ 
    public static explicit operator float(TwFix32 x) { ... } 
} 

posso convertire un TwFix32 a int con un unico cast esplicito: (int)fix32

Ma per convertirlo in decimale, ho utilizzare due calchi: (decimal)(float)fix32

Non esiste alcuna conversione implicita da float a int o decimale. Perché il compilatore mi consente di omettere il cast intermedio per farlo fluttuare quando sto andando a int, ma non quando decimale?

risposta

8

Sono spesso in perdita per dare una risposta soddisfacente alle domande "perché".

Il motivo per cui il compilatore C# mostra questo comportamento è perché è (in questo caso almeno (*)) un'implementazione corretta della specifica C#.

La sezione 6.4.5 della specifica descrive come vengono analizzate le conversioni definite dall'utente. Una lettura attenta di quella sezione spiegherà perché la conversione esplicita a int è legale ma non a decimale.

In particolare, il relativo paragrafo è:

Trova l'insieme di applicabile definita dall'utente e sollevò operatori di conversione, U. Questo set è composto da operatori di conversione implicita o esplicita definita dall'utente e sollevato dichiarati dal classi o strutture in D che convertono da un tipo che comprende o racchiuso da S in un tipo che comprende o racchiuso da T. Se U è vuoto, la conversione non è definita e si verifica un errore in fase di compilazione.

Nel tuo caso, S è TwFix e T è int o decimale. L'unica conversione esplicita definita dall'utente su TwFix restituisce un float. int è compreso da float, ma decimal non è compreso da né comprende float. Quindi l'insieme U ha un membro in un caso ed è vuoto nell'altro. Quindi un caso produce un errore, come dice la specifica, e l'altro no.

Ho la sensazione che questa risposta non sia soddisfacente. In caso contrario, puoi riformulare la domanda in modo che non contenga la parola "perché"? Sono molto più bravo a rispondere alle domande "what" o "how" che alle domande "why".

(*) Il compilatore ha noti bug nel codice che calcola se un tipo comprende un altro allo scopo di determinare quali conversioni incorporate sono rilevanti quando si analizza la semantica di una particolare conversione definita dall'utente. In molti casi non stiamo deliberatamente riparando questi bug perché così facendo introdurrebbe un cambiamento sostanziale nel codice del mondo reale senza grandi vantaggi. Mi piacerebbe molto rivisitare questa sezione della specifica e riscriverla in modo da rimuovere il concetto di "tipo onnicomprensivo"; è un po 'strano nelle specifiche. E, come hai scoperto, produce questa stranezza, dove float è esplicitamente convertibile in decimale e decimale è esplicitamente convertibile in float, ma dal momento che nessuno dei due comprende l'altro, il codice di conversione esplicito definito dall'utente non gli piace. Tuttavia questa è una priorità molto bassa.

+1

La mia sensazione è che la domanda di fondo è perché (sì, lo so) il fatto che ci sia una conversione implicita da int a float dovrebbe influire sulle conversioni che si verificano nella direzione opposta (da TwFix32 a virgola mobile a int). Questo mi sembra che * dovrebbe * essere una conversione irrilevante in questo caso - ma chiaramente non lo è, perché riguarda l'involucro.Sospetto che la * reale * "perché" la risposta qui sia probabile, "Perché fa funzionare molti altri casi come previsto, al costo di questo tipo di stranezze". –

+1

@Jon: corretto; se si pensa a queste regole dal punto di vista dell'analisi delle conversioni definite dall'utente su tipi * reference *, ha più senso; i tipi di riferimento non-interfaccia tipicamente non hanno situazioni come la situazione float/decimale. –

Problemi correlati