Quando dovrei usare l'operatore di conversione del tipo implicito di C #?


14

In C #, possiamo sovraccaricare l'operatore di conversione implicita come questo (esempio da MSDN ):

struct Digit
{
    /* ... */
    public static implicit operator byte(Digit d)  // implicit digit to byte conversion operator
    {
        /* ... */
    }
}

Pertanto, possiamo avere un tipo, un tipo di valore personalizzato , che si converte magicamente in un altro tipo (non correlato), lasciando lo spettatore sconcertato (fino a quando non scrutano nel backstage e vedono l'operatore di conversione implicito, cioè).

Non mi piace lasciare sconcertato chiunque legga il mio codice. Non penso che molte persone lo facciano.

La domanda è: quali sono i casi d'uso dell'operatore di conversione del tipo implicito che non renderà il mio codice molto più difficile da capire?


1
Wow. In realtà non sapevo che esistesse. Non che sia necessariamente una buona cosa da usare; So che la gente si è davvero seccata per questo tipo di funzionalità nascosta in C ++.
Katana314,

@ Katana314: Non era questo il motivo per cui le persone si infastidivano, ma per qualcuno che aggiungeva un sovraccarico (che si tratti di operatore, funzione di conversione, costruttore, funzione libera o membro) con un comportamento sorprendente, preferibilmente sottilmente sorprendente.
Deduplicatore,

Ti consiglio di leggere il "sovraccarico dell'operatore" in C ++, in particolare gli operatori "casting". Sospetto che molti degli stessi argomenti a favore / contro siano gli stessi, tranne per il fatto che il dibattito è andato avanti tre volte da quando C # è esistito con molto altro da leggere.

Risposte:


18

Vorrei solo raccomandare conversioni implicite tra tipi che rappresentano approssimativamente gli stessi valori in modi diversi. Per esempio:

  • Diversi tipi di colore come RGB, HSL, HSVe CMYK.
  • Unità diverse per la stessa quantità fisica ( Metervs Inch).
  • Diversi sistemi di coordinate (polare contro cartesiano).

Tuttavia, ci sono alcune linee guida forti che indicano quando è non è opportuno definire una conversione implicita:

  • Se la conversione causa una perdita significativa di precisione o intervallo, non dovrebbe essere implicita (ad es. Da float64 a float32 o da long a int).
  • Se la conversione può generare InvalidCastun'eccezione ( ), non dovrebbe essere implicita.
  • Se la conversione provoca un'allocazione dell'heap ogni volta che viene eseguita, non dovrebbe essere implicita.
  • Se la conversione non è O(1)un'operazione, non dovrebbe essere implicita.
  • Se il tipo di origine o il tipo di destinazione è modificabile, la conversione non dovrebbe essere implicita.
  • Se la conversione dipende da una sorta di contesto (database, impostazioni di cultura, configurazione, file system, ecc.), Non dovrebbe essere implicito (scoraggerei anche un operatore di conversione esplicito in questo caso).

Ora supponiamo che il tuo operatore di conversione f: T1 -> T2non violi nessuna delle regole precedenti, quindi il seguente comportamento indica fortemente che la conversione può essere implicita:

  • Se a == ballora f(a) == f(b).
  • Se a != ballora f(a) != f(b).
  • Se a.ToString() == b.ToString()allora f(a).ToString() == f(b).ToString().
  • Ecc. Per altre operazioni definite su entrambi T1e T2.

Tutti i tuoi esempi sono probabilmente in perdita. Che siano comunque abbastanza esatti, ...
Deduplicatore,

Sì, ho capito che :-). Non riuscivo a pensare a un termine migliore per "smarrito". Ciò che intendevo con "perdita" sono le conversioni in cui la portata o la precisione sono sostanzialmente ridotte. Ad esempio da float64 a float32 o da long a int.
Elian Ebbing,

Penso che a! = B => f (a)! = F (b), probabilmente non dovrebbe applicarsi. Ci sono molte funzioni che potrebbero restituire lo stesso valore per diversi input, floor () e ceil () ad esempio dal lato della matematica
cdkMoose

@cdkMoose Hai ragione ovviamente, ed è per questo che vedo queste proprietà più come "punti bonus", non come regole. La seconda proprietà significa semplicemente che la funzione di conversione è iniettiva. Questo accade spesso quando si converte in un tipo con un intervallo strettamente più ampio, ad esempio da int32 a int64.
Elian Ebbing,

@cdkMoose D'altra parte, la prima proprietà afferma semplicemente che due valori all'interno della stessa classe di equivalenza di T1(implicita dalla ==relazione su T1) sono sempre associati a due valori all'interno della stessa classe di equivalenza di T2. Ora che ci penso, immagino che la prima proprietà in realtà dovrebbe essere richiesta per una conversione implicita.
Elian Ebbing,

6

La domanda è: quali sono i casi d'uso dell'operatore di conversione del tipo implicito che non renderà il mio codice molto più difficile da capire?

Quando i tipi non lo sono indipendenti (dai programmatori). Esistono (rari) scenari in cui si hanno due tipi non correlati (per quanto riguarda il codice), che sono effettivamente correlati (per quanto riguarda il dominio o programmatori ragionevoli).

Ad esempio, alcuni codici per eseguire la corrispondenza delle stringhe. Uno scenario comune è quello di abbinare una stringa letterale. Piuttosto che chiamareIsMatch(input, new Literal("some string")) , una conversione implicita ti consente di sbarazzarti di quella cerimonia - il rumore nel codice - e concentrarti sulla stringa letterale.

Quasi tutti i programmatori vedranno IsMatch(input, "some string")e intuiranno rapidamente cosa sta succedendo. Rende il tuo codice più chiaro sul sito di chiamata. In breve, rende un po 'più facile capire cosa sta succedendo, a scapito di come sta succedendo.

Ora, potresti sostenere che un semplice sovraccarico di funzioni per fare la stessa cosa sarebbe meglio. E questo è. Ma se questo genere di cose è onnipresente, avere una conversione è più pulito (meno codice, maggiore coerenza) rispetto a un mucchio di sovraccarichi di funzioni.

E potresti sostenere che è meglio richiedere ai programmatori di creare esplicitamente il tipo intermedio in modo che vedano "cosa sta realmente succedendo". Questo è meno semplice. Personalmente, penso che l'esempio letterale di corrispondenza delle stringhe sia molto chiaro su "cosa sta realmente succedendo" - il programmatore non ha bisogno di conoscere la meccanica di come tutto accade. Sai come viene eseguito tutto il codice dai vari processori su cui viene eseguito il codice? C'è sempre una linea di astrazione in cui i programmatori smettono di preoccuparsi di come funziona qualcosa. Se ritieni che i passaggi della conversione implicita siano importanti, non utilizzare la conversione implicita. Se pensi che siano solo una cerimonia per rendere felice il computer e il programmatore farebbe meglio a non vedere quel rumore ovunque,


Il tuo ultimo punto può e deve essere portato ancora oltre: c'è anche una linea oltre la quale un programmatore avrebbe fatto meglio a non preoccuparsi di come si fa qualcosa, perché non è contrattuale.
Deduplicatore,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.