Tipizzazione tipica del nuovo oggetto: esiste un modo per evitare la sintassi ripetitiva?

2

Quando apprendo C # (e concetti OO più in generale allo stesso tempo) qualcosa che ho trovato molto distraente è esemplificato dalla seguente riga:

ExampleClass exampleObject = new ExampleClass();

È l'apparente ripetizione ridondante che mi ha infastidito. Qualche luce è emersa quando ho colto la differenza tra dichiarare una variabile e assegnargli un valore o riferimento (e che questi processi possono essere separati). Tuttavia, l'utilizzo di cui sopra è estremamente comune.

Ovviamente, ho imparato a conviverci (e non mi aspetto che la sintassi di C # possa cambiare!), ma mi sono chiesto se c'è un modo migliore. Ci sarebbe qualche merito in quanto segue:

exampleObject = new ExampleClass();

Dove la variabile exampleObject è strongmente digitata come ExampleClass quando non è specificata?

EDIT [dopo che i commentatori hanno sottolineato l'ambiguità di cui sopra tra una dichiarazione di una nuova e un'assegnazione di una variabile esistente:]

Un'ipotetica sintassi abbreviata alternativa, che evita l'ambiguità di dichiarazione / assegnazione, è:

ExampleClass exampleObject = new();

... dove ciò creerebbe una nuova istanza di ExampleClass , il tipo essendo stato specificato nella dichiarazione della sua variabile di riferimento, exampleObject .

.. ma sono sicuro che questo apre altri problemi a cui non ho pensato!
/ Modifica

Ovviamente, se per qualche motivo volessimo che exampleObject fosse di tipo Object , o - un requisito più comune - che fosse del tipo di una classe genitore di ExampleClass , dovremmo specificare questi tipi di fronte del nome della variabile nel modo standard, ma c'è qualche inconveniente alla mia stenografia suggerita in questo scenario molto comune?

    
posta Cantabrigian 18.04.2014 - 12:06
fonte

3 risposte

9

C # richiede di dichiarare una variabile in modo esplicito, oltre ad assegnargli un valore. Questo è fatto in modo che sia più chiaro sia al compilatore che alla persona che legge il codice che stiamo definendo una variabile nuova qui, non usando una variabile precedentemente definita, che può semplificare l'analisi e creare il codice molto più chiaro.

Come hai detto, l'affermazione ExampleClass c = new ExampleClass() è semplicemente una contrazione di entrambe queste azioni, motivo per cui senti la duplicazione: sono due azioni diverse. Ma si sente goffo. Questo è il motivo per cui le specifiche C # 3.0 (in VS2008) hanno aggiunto la parola chiave var , che consente salti molta della verbosità, anche se non era la motivazione principale per la funzione.

Usando var , il nostro codice di esempio diventa var c = new ExampleClass() - molto più facile da analizzare, ma ancora dichiarando esplicitamente e assegnando. Diventa ancora più utile con nomi di classi lunghi - var complexDictionary = new Dictionary<Tuple<int,int>, List<string>>() , ad esempio. Immagina di scrivere due volte!

Ovviamente, var è stato originariamente introdotto per supportare i tipi anonimi . Questi sono usati dalle espressioni lambda e LINQ, e creano tipi con nomi che sono sia maldestri ( MyMethod<>Class5 o simili) che non noti allo sviluppatore, in modo da risparmiare un sacco di problemi.

Inoltre, nella tua modifica, hai proposto una soluzione simile ma inversa alla duplicazione - per mantenere esplicita la dichiarazione , ma la istanza implicita. Ciò consente di risparmiare un numero di caratteri pari a var , ma è molto meno flessibile. Considera che la dichiarazione delle variabili è molto semplice: devi semplicemente indicare il tipo di variabile. L'inizializzazione, tuttavia, è più complessa. A volte usi semplicemente new , quindi possiamo usare la sintassi new() . Ma spesso abbiamo diversi parametri di costruzione. O che ne dici di inizializzare usando un metodo factory, come ExampleClass exampleObject = ExampleBuilder.CreateExample(); ? La tua sintassi non ci salva nulla lì, mentre l'inferenza del tipo in var ci permetterà di usare var exampleObject = ExampleBuilder.CreateExample(); senza problemi.

    
risposta data 18.04.2014 - 13:04
fonte
4

No, la sintassi ripetitiva non dovrebbe infastidire nessuno. È un po 'come lamentarsi delle tre affermazioni all'interno di un ciclo for .

for(int x = 0; x <= 100; x++) {
   /* do work */
}

Certo, potresti usare lo zucchero sintattico per deridere questo schema comune come sotto, ma non guadagni molto con qualsiasi alternativa sensata e, come ogni uso di zucchero, perdi alcune cose che sono importanti.

for(x = 0 to 100) {
   /* do work */
}

Sebbene ciò sia utile per la semplice iterazione, le carenze diventano chiare dopo un po 'di riflessione:

  1. Non è possibile nella stenografia che x sia tutto tranne una variabile dichiarata localmente.
  2. Non puoi avere un test più complesso, come "salta numeri primi"
  3. Non puoi passare implicitamente di più di 1 o incrementare qualche modificatore esterno.

La maggior parte di questi può essere superata, ma la sintassi non aiuta molto al di là della digitazione.

Probabilmente non vale niente che i tuoi esempi di una sola riga distorcano effettivamente il modo in cui le cose dovrebbero essere fatte in C #. Se inizializziamo un membro di una classe, vorrai davvero fare qualcosa di simile al seguente:

class Test : ITest {
    public IExample example;
    public Test() {
      example = new Example();
    }
}
  1. Il tipo di membro viene dichiarato tramite un'interfaccia, quindi puoi cambiare la classe concreta senza riscrivere il codice principale.
  2. L'inizializzazione effettiva si trova in un costruttore, che può essere regolato o sovrascritto in una classe derivante.
  3. Parole come "Oggetto" e "Classe" vengono saltate del tutto. I nomi sono per le persone e la sintassi di capitalizzazione java fa abbastanza per i nostri scopi.

Al contrario, se stai solo inizializzando un'istanza locale, dovresti eseguire una delle seguenti operazioni:

IExample example = new Example();

o

var example = new Example();
    
risposta data 18.04.2014 - 15:08
fonte
3

Sì, disturba le altre persone. È una delle principali motivazioni per le persone che scelgono lingue tipizzate dinamicamente. Quello di cui stai parlando nel contesto di un linguaggio tipizzato staticamente è chiamato inferenza di tipo , che in pratica significa che il compilatore può dire che tipo una variabile è di che tipo è inizializzata. È stata una caratteristica completa in lingue come Scala e Haskell fin dall'inizio, a causa dei pazzi tipi lunghi che si presentano regolarmente in quelle lingue. C # lo supporta in una forma più limitata con var e C ++ 11 con auto .

Tuttavia, si noti che nelle lingue con inferenza di tipo, si raccomanda comunque di specificare esplicitamente i tipi sulle interfacce pubbliche per il proprio codice. Ti impedisce di cambiare accidentalmente l'interfaccia pubblica, diciamo se qualcosa è implementato in termini di un tipo concreto, ma vuoi che la sua interfaccia pubblica sia di tipo più astratto. Rende anche più semplice per gli utenti del tuo codice determinare come usarlo.

    
risposta data 18.04.2014 - 15:02
fonte

Leggi altre domande sui tag