Per analogia, C # è fondamentalmente come un insieme di strumenti meccanici in cui qualcuno ha letto che in genere si dovrebbero evitare pinze e chiavi regolabili, quindi non include affatto le chiavi regolabili e le pinze sono bloccate in uno speciale cassetto contrassegnato "non sicuro" e può essere utilizzato solo con l'approvazione di un supervisore, dopo aver firmato una dichiarazione di non responsabilità che ha assolto il datore di lavoro da qualsiasi responsabilità per la propria salute.
C ++, al confronto, non include solo le chiavi e le pinze regolabili, ma alcuni strumenti per scopi speciali a sfera dispari il cui scopo non è immediatamente evidente, e se non si conosce il modo giusto per tenerli, potrebbero facilmente taglia il pollice (ma una volta capito come usarli, puoi fare cose che sono essenzialmente impossibili con gli strumenti di base nella toolbox C #). Inoltre, ha un tornio, una fresatrice, una smerigliatrice di superficie, una sega a nastro per il taglio di metalli ecc., Per permetterti di progettare e creare strumenti completamente nuovi ogni volta che ne senti il bisogno (ma sì, gli strumenti di questi macchinisti possono e causeranno ferite gravi se non sai cosa stai facendo con loro - o anche se sei semplicemente incauto).
Ciò riflette la differenza fondamentale in filosofia: C ++ tenta di darti tutti gli strumenti di cui potresti avere bisogno per qualsiasi progetto tu voglia. Non fa quasi nessun tentativo di controllare come si usano questi strumenti, quindi è anche facile usarli per produrre disegni che funzionano bene solo in rare situazioni, così come progetti che sono probabilmente solo un'idea pessima e nessuno sa di una situazione in cui probabilmente funzioneranno bene. In particolare, gran parte di questo viene fatto disaccoppiando le decisioni di progettazione - anche quelle che in pratica sono quasi sempre accoppiate. Di conseguenza, c'è un'enorme differenza tra la semplice scrittura di C ++ e la scrittura di C ++. Per scrivere bene in C ++, è necessario conoscere molti idiomi e regole empiriche (comprese le regole pratiche su come seriamente riconsiderare prima di infrangere altre regole empiriche). Di conseguenza, il C ++ è orientato molto più verso la facilità d'uso (da parte degli esperti) rispetto alla facilità di apprendimento. Ci sono anche (troppe) circostanze in cui non è nemmeno terribilmente facile da usare.
C # fa molto di più per provare a forzare (o almeno estremamente suggerisce strongmente) ciò che i progettisti di linguaggio hanno considerato buone pratiche di progettazione. Molte cose che sono disaccoppiate in C ++ (ma di solito vanno insieme nella pratica) sono direttamente accoppiate in C #. Permette al codice "non sicuro" di spingere un po 'i limiti, ma onestamente, non molto.
Il risultato è che da un lato ci sono alcuni progetti che possono essere espressi in modo abbastanza diretto in C ++ che sono sostanzialmente più goffi da esprimere in C #. D'altra parte, è un lotto tutto più facile da imparare in C #, e le possibilità di produrre un disegno davvero orribile che non funzioni per la tua situazione (o probabilmente altre) sono drasticamente ridotto. In molti casi (probabilmente anche nella maggior parte dei casi), è possibile ottenere un design solido e funzionale semplicemente "andando con il flusso", per così dire. Oppure, come uno dei miei amici (almeno mi piace pensare a lui come ad un amico - non sono sicuro che sia d'accordo) a lui piace, C # rende facile cadere nella fossa del successo.
Quindi, esaminando in modo più specifico la questione di come class
e struct
hanno ottenuto come sono nei due linguaggi: oggetti creati in una gerarchia ereditaria in cui si potrebbe usare un oggetto di una classe derivata sotto le spoglie della sua base classe / interfaccia, sei praticamente bloccato dal fatto che normalmente devi farlo tramite una sorta di puntatore o riferimento - a livello concreto, ciò che accade è che l'oggetto della classe derivata contiene qualcosa di memoria che può essere trattato come un'istanza della classe base / interfaccia e l'oggetto derivato viene manipolato tramite l'indirizzo di quella parte di memoria.
In C ++, spetta al programmatore farlo correttamente - quando usa l'ereditarietà, spetta a lui assicurarsi che (per esempio) una funzione che lavora con le classi polimorfiche in una gerarchia lo faccia tramite un puntatore o un riferimento alla classe base.
In C #, ciò che è fondamentalmente la stessa separazione tra i tipi è molto più esplicito e applicato dal linguaggio stesso. Il programmatore non ha bisogno di prendere alcuna iniziativa per passare un'istanza di una classe per riferimento, perché ciò avverrà per impostazione predefinita.