Perché i parametri const non sono consentiti in C #?


102

Sembra strano soprattutto per gli sviluppatori C ++. In C ++ abbiamo usato per contrassegnare un parametro come constper essere sicuri che il suo stato non verrà modificato nel metodo. Ci sono anche altri motivi specifici per C ++, come passare const refper passare per ref ed essere sicuri che lo stato non verrà modificato. Ma perché non possiamo contrassegnare come parametri del metodo const in C #?

Perché non posso dichiarare il mio metodo come segue?

    ....
    static void TestMethod1(const MyClass val)
    {}
    ....
    static void TestMethod2(const int val)
    {}
    ....

È sempre stata solo una caratteristica di sicurezza in C ++ e non è mai stata veramente parte integrante del linguaggio come l'ho visto. Gli sviluppatori di C # pensavano che evidentemente non aggiungesse molto valore.
Noldorin

3
Una grande discussione a questa domanda: "Const-correttezza in C #": stackoverflow.com/questions/114149/const-correctness-in-c
tenpn

C'è per i tipi di valore [out / ref], ma non per i tipi di riferimento. È una domanda interessante.
kenny

4
Come può questa domanda avere sia i tag C # che indipendenti dal linguaggio?
CJ7

1
Dati immutabili e funzioni senza effetti collaterali sono più facili da ragionare. Potrebbe piacerti
Colonel Panic

Risposte:


59

Oltre alle altre buone risposte, aggiungerò ancora un altro motivo per non mettere la costanza in stile C in C #. Tu hai detto:

contrassegniamo il parametro come const per essere sicuri che il suo stato non verrà modificato nel metodo.

Se const lo facesse davvero, sarebbe fantastico. Const non lo fa. La const è una bugia!

Const non fornisce alcuna garanzia che io possa effettivamente utilizzare. Supponi di avere un metodo che accetta una cosa const. Sono disponibili due autori del codice: la persona che scrive il chiamante e la persona che scrive il chiamato . L'autore del chiamato ha fatto prendere al metodo una const. Cosa possono presumere i due autori sia invariante rispetto all'oggetto?

Niente. Il chiamato è libero di eliminare const e mutare l'oggetto, quindi il chiamante non ha alcuna garanzia che chiamare un metodo che accetta un const in realtà non lo muterà. Allo stesso modo, il chiamato non può presumere che il contenuto dell'oggetto non cambierà durante l'azione del chiamato; il chiamato potrebbe chiamare qualche metodo mutante su un alias non const dell'oggetto const, e ora il cosiddetto oggetto const è cambiato .

La const in stile C non fornisce alcuna garanzia che l'oggetto non cambierà e quindi è rotto. Ora, C ha già un sistema di tipi deboli in cui puoi reinterpretare il cast di un double in un int, se lo vuoi davvero, quindi non dovrebbe sorprendere che abbia un sistema di tipi debole rispetto a const. Ma C # è stato progettato per avere un buon sistema di tipi, un sistema di tipi in cui quando dici "questa variabile contiene una stringa", la variabile contiene effettivamente un riferimento a una stringa (o null). Non vogliamo assolutamente inserire un modificatore "const" in stile C nel sistema dei tipi perché non vogliamo che il sistema dei tipi sia una bugia . Vogliamo che il sistema di tipi sia forte in modo che tu possa ragionare correttamente sul tuo codice.

Cost in C è una linea guida ; in pratica significa "puoi fidarti di me per non provare a mutare questa cosa". Non dovrebbe essere nel sistema dei tipi ; le cose nel sistema di tipi dovrebbero essere un fatto sull'oggetto su cui puoi ragionare, non una linea guida al suo utilizzo.

Ora, non fraintendermi; solo perché const in C è profondamente rotto non significa che l'intero concetto sia inutile. Quello che mi piacerebbe vedere è una forma effettivamente corretta e utile di annotazione "const" in C #, un'annotazione che sia gli umani che i compilatori potrebbero usare per aiutarli a comprendere il codice e che il runtime potrebbe usare per fare cose come la paralellizzazione automatica e altre ottimizzazioni avanzate.

Ad esempio, immagina di poter "disegnare una scatola" attorno a un pezzo di codice e dire " Garantisco che questo pezzo di codice non esegue mutazioni a nessun campo di questa classe" in un modo che potrebbe essere controllato dal compilatore. Oppure disegna una scatola che dice "questo metodo puro muta lo stato interno dell'oggetto ma non in alcun modo che sia osservabile fuori dalla scatola". Un oggetto del genere non può essere automaticamente multi-thread in modo sicuro, ma può essere memorizzato automaticamente . Ci sono tutti i tipi di annotazioni interessanti che potremmo inserire nel codice che consentirebbero ottimizzazioni complete e una comprensione più approfondita. Possiamo fare molto meglio della debole annotazione const in stile C.

Tuttavia, sottolineo che questa è solo una speculazione . Non abbiamo piani fissi per inserire questo tipo di funzionalità in qualsiasi ipotetica versione futura di C #, se ce n'è una, che non abbiamo annunciato in un modo o nell'altro. È qualcosa che mi piacerebbe vedere e qualcosa che potrebbe richiedere l'imminente enfasi sull'elaborazione multi-core, ma niente di tutto ciò dovrebbe essere in alcun modo interpretato come una previsione o una garanzia di qualsiasi caratteristica particolare o direzione futura per C #.

Ora, se quello che vuoi è semplicemente un'annotazione sulla variabile locale che è un parametro che dice "il valore di questo parametro non cambia in tutto il metodo", allora, certo, sarebbe facile. Potremmo supportare locals "readonly" e parametri che sarebbero inizializzati una volta, e un errore in fase di compilazione da modificare nel metodo. La variabile dichiarata dall'istruzione "using" è già di questo tipo locale; potremmo aggiungere un'annotazione opzionale a tutti i locali e parametri per farli agire come variabili "usando". Non è mai stata una funzionalità con priorità molto alta, quindi non è mai stata implementata.


34
Mi dispiace, ma trovo questo argomento molto debole, il fatto che uno sviluppatore possa dire una bugia non può essere evitato in nessuna lingua. void foo (const A & a) che modifica l'oggetto a non è diverso da int countApples (Basket b) che restituisce il numero di pere. È solo un bug, un bug che si compila in qualsiasi lingua.
Alessandro Teruzzi

55
"Il chiamato è libero di gettare via il const ..." uhhhhh ... (° _ °) Questo è un argomento piuttosto superficiale che stai facendo qui. Il chiamato è anche libero di iniziare a scrivere posizioni di memoria casuali con 0xDEADBEEF. Ma entrambi sarebbero una pessima programmazione, e colti presto e in modo acuto da qualsiasi compilatore moderno. In futuro, quando scrivi le risposte, per favore non confondere ciò che è tecnicamente possibile usando le backdoor di una lingua con ciò che è fattibile nel codice reale. Informazioni distorte come questa confondono i lettori meno esperti e degradano la qualità delle informazioni su SO.
Slipp D. Thompson

8
"Cost in C è una linea guida;" Citare una fonte per alcune delle cose che stai dicendo aiuterebbe davvero il tuo caso qui. Nella mia formazione e nella mia esperienza nel settore, l'affermazione citata è hogwash - const in C è una caratteristica incorporata obbligatoria tanto quanto il sistema di tipi stesso - entrambi hanno backdoor per ingannarli quando necessario (come in realtà tutto in C) ma ci si aspetta essere utilizzato dal libro nel 99,99% del codice.
Slipp D. Thompson

29
Questa risposta è il motivo per cui a volte odio il design C #. I progettisti del linguaggio sono assolutamente sicuri di essere molto più intelligenti di altri sviluppatori e cercano di proteggerli eccessivamente dai bug. È ovviamente che la parola chiave const funziona solo in fase di compilazione, perché in C ++ abbiamo accesso diretto alla memoria e possiamo fare tutto ciò che vogliamo e abbiamo diversi modi per ottenere la dichiarazione const. Ma nessuno lo fa in situazioni normali. A proposito, anche "private" e "protected" in C # non forniscono alcuna garanzia, perché possiamo accedere a questi metodi o campi usando la reflection.
BlackOverlord

13
@BlackOverlord: (1) Il desiderio di progettare un linguaggio le cui proprietà portino naturalmente gli sviluppatori al successo piuttosto che al fallimento è motivato dal desiderio di costruire strumenti utili , non dalla convinzione che i designer siano più intelligenti dei loro clienti, come ipotizzi. (2) Reflection non fa parte del linguaggio C #; fa parte del runtime; l'accesso alla reflection è limitato dal sistema di sicurezza del runtime. Il codice a bassa attendibilità non ha la possibilità di eseguire riflessioni private. Le garanzie fornite dai modificatori di accesso sono per codice bassa affidabilità ; il codice ad alta affidabilità può fare qualsiasi cosa.
Eric Lippert,

24

Uno dei motivi per cui non c'è correttezza const in C # è perché non esiste a livello di runtime. Ricorda che C # 1.0 non disponeva di alcuna funzionalità a meno che non facesse parte del runtime.

E diversi motivi per cui il CLR non ha una nozione di correttezza const sono ad esempio:

  1. Complica il runtime; inoltre, anche la JVM non ce l'aveva, e il CLR fondamentalmente è iniziato come un progetto per creare un runtime simile a JVM, non un runtime simile a C ++.
  2. Se c'è la correttezza const a livello di runtime, dovrebbe esserci la correttezza const nel BCL, altrimenti la funzionalità è praticamente inutile per quanto riguarda .NET Framework.
  3. Ma se BCL richiede la correttezza const, ogni linguaggio sopra il CLR dovrebbe supportare la correttezza const (VB, JavaScript, Python, Ruby, F #, ecc.) Questo non accadrà.

La correttezza costante è praticamente una caratteristica del linguaggio presente solo in C ++. Quindi praticamente si riduce alla stessa argomentazione sul motivo per cui il CLR non richiede eccezioni controllate (che è una caratteristica solo del linguaggio Java).

Inoltre, non credo che sia possibile introdurre una funzionalità di sistema di tipo così fondamentale in un ambiente gestito senza interrompere la compatibilità con le versioni precedenti. Quindi non contare sulla correttezza delle cost per entrare nel mondo C #.


Sei sicuro che JVM non ce l'abbia. Come so che ce l'ha. Puoi provare public static void TestMethod (final int val) in Java.
Incognito

2
Finale non è proprio const. È ancora possibile modificare i campi sull'IIRC di riferimento, ma non il valore / riferimento stesso. (Che è comunque passato per valore, quindi è più un trucco del compilatore per impedirti di modificare il valore del parametro.)
Ruben

1
il tuo terzo proiettile è vero solo in parte. avere parametri const per le chiamate BCL non sarebbe un cambiamento radicale poiché descrivono semplicemente il contratto in modo più preciso. "Questo metodo non cambierà lo stato dell'oggetto" in contrasto con "Questo metodo richiede di passare un valore const" che sarebbe interrotto
Rune FS

2
Viene applicato all'interno del metodo, quindi non sarebbe un cambiamento radicale introdurlo nel BCL.
Rune FS

1
Anche se il runtime non potesse imporlo, sarebbe utile avere un attributo che specifichi se una funzione dovrebbe / ci si deve permettere di scrivere su un refparametro (specialmente, nel caso di metodi / proprietà struct, this). Se un metodo specifica specificamente che non scriverà su un refparametro, il compilatore dovrebbe consentire il passaggio di valori di sola lettura. Al contrario, se un metodo afferma che scriverà this, il compilatore non dovrebbe consentire che tale metodo venga invocato su valori di sola lettura.
supercat

12

Credo che ci siano due ragioni per cui C # non è corretto per la configurazione.

Il primo è la comprensibilità . Pochi programmatori C ++ comprendono la correttezza const. Il semplice esempio di const int argè carino, ma ho anche visto char * const * const arg: un puntatore costante a puntatori costanti a caratteri non costanti. La correttezza delle costanti sui puntatori alle funzioni è un nuovo livello di offuscamento.

Il secondo è perché gli argomenti della classe sono riferimenti passati per valore. Ciò significa che ci sono già due livelli di costanza da affrontare, senza una sintassi ovviamente chiara . Un punto di inciampo simile sono le raccolte (e le raccolte di raccolte, ecc.).

La correttezza della costanza è una parte importante del sistema di tipi C ++. Potrebbe, in teoria, essere aggiunto a C # come qualcosa che viene controllato solo in fase di compilazione (non è necessario aggiungerlo al CLR e non influirebbe sul BCL a meno che non fosse inclusa la nozione di metodi membri const) .

Tuttavia, credo che ciò sia improbabile: la seconda ragione (sintassi) sarebbe abbastanza difficile da risolvere, il che renderebbe la prima ragione (comprensibilità) ancora più un problema.


1
Hai ragione sul fatto che CLR non ha davvero bisogno di preoccuparsi di questo, in quanto si possono usare modopte modreqa livello di metadati per memorizzare quelle informazioni (come già fa C + / CLI - vedi System.Runtime.CompilerServices.IsConst); e questo potrebbe essere banalmente esteso anche ai metodi const.
Pavel Minaev

Vale la pena ma deve essere fatto in modo sicuro. La const Deep / Shallow di cui parli apre un'intera lattina di vermi.
user1496062

In un c ++ in cui i riferimenti vengono effettivamente manipolati, posso vedere il tuo argomento sull'avere due tipi di const. Tuttavia, in C # (dove devi passare attraverso backdoor per usare "puntatori") mi sembra abbastanza chiaro che ci sia un significato ben definito per i tipi di riferimento (cioè le classi) e un significato ben definito anche se diverso per il valore tipi (cioè primitive, strutture)
Assimilater

2
I programmatori che non sono in grado di comprendere const-ness non dovrebbero programmare in C ++.
Steve White

5

constsignifica "costante del tempo di compilazione" in C #, non "di sola lettura ma eventualmente modificabile da altro codice" come in C ++. Un analogo approssimativo del C ++ constin C # è readonly, ma è applicabile solo ai campi. A parte questo, non esiste affatto una nozione simile a C ++ di correttezza const in C #.

La logica è difficile da dire con certezza, perché ci sono molte potenziali ragioni, ma la mia ipotesi sarebbe il desiderio di mantenere il linguaggio semplice prima di tutto e i vantaggi incerti dell'implementazione di questo secondo.


Quindi pensi che MS consideri come "rumore" avere parametri const nei metodi.
Incognito

1
Non sono sicuro di cosa sia "rumore", preferisco chiamarlo "elevato rapporto costi / benefici". Ma, ovviamente, avrai bisogno di qualcuno del team C # che te lo dica con certezza.
Pavel Minaev

Quindi, da quanto ho capito, il tuo argomento è che la costanza è stata lasciata fuori da C # per mantenerla semplice. Pensaci.
Steve White,

2

Ciò è possibile a partire da C # 7.2 (dicembre 2017 con Visual Studio 2017 15.5).

Solo per Struct e tipi di base ! , non per i membri delle classi.

È necessario utilizzare inper inviare l'argomento come input per riferimento. Vedere:

Vedi: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/in-parameter-modifier

Per il tuo esempio:

....
static void TestMethod1(in MyStruct val)
{
    val = new MyStruct;  // Error CS8331 Cannot assign to variable 'in MyStruct' because it is a readonly variable
    val.member = 0;  // Error CS8332 Cannot assign to a member of variable 'MyStruct' because it is a readonly variable
}
....
static void TestMethod2(in int val)
{
    val = 0;  // Error CS8331 Cannot assign to variable 'in int' because it is a readonly variable
}
....
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.