Perché c'è un nuovo () vincolo in C # ma nessun altro vincolo simile?


19

In generici C #, possiamo dichiarare un vincolo per un parametro di tipo Tcon un costruttore predefinito, dicendo where T : new(). Tuttavia, nessun altro tipo di vincoli come questo è valido, new(string)ad esempio ecc.

Dal punto di vista della progettazione linguistica e / o dell'implementazione, qual è la ragione?

C'è qualcosa nel modo in cui funzionano i costruttori o nel modo in cui viene implementato il sistema di tipi che lo proibisce (o almeno lo rende più difficile)? Se è così, che cosa è? Ricordo di aver letto da qualche parte che in default(T)realtà si compila new T()per T : struct. È legato a questo, forse?

O è semplicemente una decisione di progettazione presa per evitare di rendere il linguaggio troppo complicato?


new(string)non è un vincolo del costruttore predefinito. La tua domanda equivale a dire "Perché non ci sono vincoli che richiedono firme del costruttore specifiche?" Molto probabilmente perché un tale vincolo non sarebbe particolarmente utile.
Robert Harvey,

3
@RobertHarvey Sì, è esattamente quello che sto dicendo. Sto essenzialmente chiedendo se c'è qualcosa che rende speciali i costruttori di default in termini di implementazione o se è stata solo una scelta arbitraria includere questo e non l'altro.
Theodoros Chatzigiannakis,

I costruttori predefiniti sono utili in alcuni modi specifici e importanti. Ad esempio, rendono un tipo prontamente serializzabile.
Robert Harvey,

6
Firme specifiche del ctor potrebbero essere utili. Ad esempio, se la tua variabile di tipo è vincolata a essere della raccolta <T> con un ctor di T (raccolta <T>), sai che puoi costruire nuove raccolte se ne ha un'altra. Se tale utilità valga la complessità aggiuntiva, tuttavia, è una domanda.
Phoshi,

Risposte:


5

Per una risposta autorevole, vorrei fare riferimento alla risposta di Eric Lippert a quella domanda su StackOverflow qualche anno fa, di cui un breve frammento è brevemente citato di seguito.

Tuttavia, in questo caso specifico, posso certamente darvi alcuni motivi per cui rinuncerei alla funzione se venisse presentata in una riunione di progettazione come possibile funzionalità per una versione futura del linguaggio.

...

Dico che dovremmo o fare l'intera funzione o non farlo affatto. Se è importante essere in grado di limitare i tipi per avere costruttori particolari, allora facciamo l'intera funzione e limitiamo i tipi sulla base dei membri in generale e non solo dei costruttori.


2
Quella citazione, estratta dal contesto, è molto fuorviante. Suggerisce che Eric pensasse che Microsoft avrebbe dovuto "andare fino in fondo", che non è affatto la sua posizione. È, infatti, esattamente contrario alla caratteristica proposta.
Robert Harvey,

1
Grazie, questo risponde parzialmente alla mia domanda: verso la fine della sua risposta, Eric Lippert afferma che si tratta di una limitazione dell'IL e l'inclusione di questa funzione richiederebbe un'aggiunta all'IL. Sarebbe perfetto se tu potessi fornire una fonte su ciò che IL viene generato per la parte che viene implementata, cioè genericamente chiamare un costruttore predefinito.
Theodoros Chatzigiannakis,

@TheodorosChatzigiannakis: Perché non accendi il Decompilatore di Telerik e lo scopri da solo?
Robert Harvey,

2
@RobertHarvey Non credo che gli suggerisca affatto una posizione, ma ho incluso una citazione aggiuntiva per enfatizzare la sua posizione.
Chris Hannon,

1
Hmm, F # ha metodi più avanzati per vincolare un tipo , come il controllo come tempo di compilazione se una classe ha un operatore. Forse, F # ha bisogno di un sistema di vincoli più potente di C # a causa del suo controllo del tipo molto rigoroso. Tuttavia, questo linguaggio è in grado di implementare modi avanzati per conservare classe su .Net Framework.
Onesimus Nessun impegno il

16

La decompilazione (come suggerito da Robert Harvey) ha prodotto quanto segue, per chiunque fosse interessato. Questo metodo:

static T GenericMake<T>()
    where T : new()
{
    return new T();
}

Apparentemente, una volta compilato, diventa questo:

private static T GenericMake<T>()
    where T : new()
{
    T t;
    T t1 = default(T);
    if (t1 == null)
    {
        t = Activator.CreateInstance<T>();
    }
    else
    {
        t1 = default(T);
        t = t1;
    }
    return t;
}
  • Se Tè un tipo di valore, new()diventa default(T).
  • Se Tè un tipo di riferimento, new()funziona con la riflessione. Activator.CreateInstance()chiama internamente RuntimeType.CreateInstanceDefaultCtor().

Quindi eccolo: internamente, i costruttori predefiniti sono davvero speciali in C # in relazione al CLR. Dare ad altri costruttori lo stesso trattamento sarebbe stato costoso, anche se ci fossero alcuni casi d'uso validi per vincoli più complessi in generici.


4
Interessante. Perché la chiamata duplicata a default(T) per i tipi di valore?
Avner Shahar-Kashtan,

2
@ AvnerShahar-Kashtan Non lo so. Potrebbe essere un artefatto del processo di compilazione / decompilazione, come la tvariabile (che potrebbe essere sostituita da returnistruzioni nidificate ).
Theodoros Chatzigiannakis,
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.