Cum face C # generice runtime?

Un lucru care mă irită despre Java este implementarea îngrozitoare a traducerii în timp util a tipurilsau de argumente generice.

Pot observa și înțelege că implementarea C# este mult mai bună, dar sunt confuză cu privire la modul în care funcționează.

În esență, cum puteți spune:

T t = new T()

Dacă nu cunoașteți tipul de T și prin urmare nu cunoașteți cerințele argumentului constructsauului?

Pot vedea

Class cl = T.class

sau

T[] tarr = new T[0]

dar nu văd cum puteți crea într-adevăr o nouă instanță a lui T dacă nu cunoașteți cerințele de construire a acestuia?

0
Nu este, atât timp cât mergeți pe traseul C# și constrângeți T să aibă un anumit tip de constructor. Citiți răspunsurile de mai jos.
adăugat autor bgroenks, sursa
Mi se pare incredibil că tratați Tt = new T() ca o caracteristică dezirabilă. Mi se pare că este o rețetă pentru un cod care nu poate fi întrerupt.
adăugat autor corsiKa, sursa

4 răspunsuri

Nu puteți spune new T() dacă nu constrângeți tipul dvs. generic pentru a avea un constructor fără parametri utilizând codul în care constrângerea T: new() - vedeți Constrângeri privind parametrii de tip .

Și nu există "cerințe argument constructor", deoarece singurul constructor acceptat este cel fără parametri. Nu puteți folosi, de exemplu, new T (false) - constrângerile formularului unde T: new (bool) nu sunt permise.

0
adăugat

Puteți face numai new T (); dacă T este constrânsă să aibă un constructor public simplu, fără parametri, de exemplu:

public class Foo where T : new() {
    private myT = new T();
}

În plus, nu există nicio modalitate de a preciza că există vreun alt tip de constructor. Acest lucru nu este posibil:

// Doesn't work
public class Foo where T : new(String, Int) {
    private myT = new T("Foo", 5);
}

În celelalte puncte, acesta este modul în care obțineți tipul de T în timpul execuției:

var tType = typeof(T);

și crearea unui set de T nu creează nicio instanță (cu excepția cazului în care T este un tip de valoare, caz în care creează valoarea implicită a acelui tip):

// Space for 32 T's, but nothing in the array.
// If T is a value type, like an int for instance, 
// each one would have the default value (0 for int, for example)
var arrayOfT = new T[32];
0
adăugat
Dreapta. Doar am subliniat din perspectiva dezvoltatorului Java ce a făcut sens și ce nu. Deci, practic, puteți constrânge T să fie acolo unde este necesar un anumit tip de construcție. Are sens. De ce nu a făcut-o soarele?
adăugat autor bgroenks, sursa
A se vedea Sun proiectat generice Java, astfel încât acestea nu ar trebui să schimbe formatul fișierului .class. Dar au ajuns să facă așa oricum și nu s-au întors să-și reproșeze generice. Deci, acum suntem blocați cu nimic bun pentru nimic generic în cazul în care totul este presupus a fi o superclază constrânsă sau doar Obiect.
adăugat autor bgroenks, sursa
Sincer, C# a învățat din multe greșeli ale Java. Java a vrut să facă cât mai puține modificări JVM posibil, astfel încât au folosit tip de ștergere (în cazul în care T devine Obiect ) ... care durează mai puțin de schimbare a JVM, dar în același timp, îl ține de la cunoașterea tipului real de lucruri.
adăugat autor cHao, sursa
Probabil deoarece Java implementează generice folosind ștergerea de tip. În timpul execuției, Java tratează toți parametrii generici ca Object , deci nu are informațiile necesare pentru a construi un T . În .NET, tipul de T este cunoscut la timpul de execuție și astfel crearea unei instanțe este destul de trivială.
adăugat autor Chris Shain, sursa
@cHao ai dreptate și genericele nu sunt singura caracteristică implementată ca extensie de compilator. Nu sunt deloc de acord cu această alegere (gândirea JVM ca mediu de compilatoare încrucișată). Mai mult decât atât, cred că compilatorul ar putea face toate lucrurile, indiferent de modul în care sunt generice implementate, așa că nu pot să înțeleg de ce avem un astfel de suport slab.
adăugat autor Adriano Repetti, sursa

De fapt, cereți compilatorului să forțeze T pentru a avea un constructor fără parametru, astfel încât să știe că puteți să new T() . De exemplu:

class Test
{
    T Create()
    {
        return new T();
    }
}

Nu se compilează deoarece compilatorul nu poate fi sigur că T nu va fi o clasă abstractă și că va avea un constructor implicit. Pentru a face acest lucru, trebuie să adăugați o constrângere pe tipul real de T:

class Test where T : new()

Acum, compilatorul va forța T să fie o clasă non abstract, cu un constructor implicit. De exemplu, acest cod nu este valabil deoarece tipul dat este abstract:

abstract class AnotherTest
{
    public void Test()
    {
        Test test = new Test();
    }
}

Din nou, dacă încercați să utilizați o clasă fără constructor implicit, compilatorul va emite o eroare:

class AnotherTest
{
    public AnotherTest(string someParameter)
    {
    }

    public void Test()
    {
        Test test = new Test();
    }
}

Cu o matrice este ceva diferit. De fapt, pur și simplu cereți compilatorului să rezerve memoria pentru un anumit număr de sloturi, nu alocați memoria pentru acele obiecte (în cazul tipurilor de referință, pur și simplu puneți null în fiecare slot).

References on MSDN

0
adăugat

new T() is just a syntactical sugar for Activator.CreateInstance()

0
adăugat
Da și nu. Primul va compila numai dacă T este constrâns să aibă un constructor public fără parametri. Acesta din urmă se va compila indiferent dacă T are sau nu o astfel de constrângere, dar va eșua la rulare dacă tipul T nu are un constructor public fără parametri.
adăugat autor supercat, sursa
@cHao: Nu pare să existe o astfel de constrângere. Apel la ex. Activator.CreateInstance , folosind cu siguranta formularul generic, se va compila foarte bine, dar arunca MissingMethodException cand se executa. Rețineți că permiterea compilării unui astfel de cod este utilă, deoarece permite cazurile în care codul ar putea dori să se ocupe de diferite tipuri diferite și ar trebui doar să apeleze constructorul implicit pentru tipurile care suportă unul.
adăugat autor supercat, sursa
@cHao: Ca un exemplu simplu, s-ar putea să existe o clasă de colectare a unei arhitecturi rare, care trebuie să fie capabilă să populeze elemente cu instanțe non-nulă; un constructor ar trebui să accepte un delegat al metodei din fabrică. Ar fi convenabil să existe o supraîncărcare a constructorului care nu necesită delegatul, dar ar putea folosi pur și simplu new() . Nu există nicio modalitate, însă, de a limita utilizarea în timp util a unei astfel de suprasarcini la tipurile care suportă new() .
adăugat autor supercat, sursa
@supercat: Aparent CreateInstance are o constrângere unde T: new() . Având în vedere că aveți tipul la timpul de compilare, se pare că ar eșua cecul atunci și acolo. S-ar putea să confundăm aici CreateInstance cu CreateInstance (Type) aici.
adăugat autor cHao, sursa
@supercat: Ahh. Da, misterează puțin. : P Exemplul prezintă o constrângere, dar documentația metodei în sine nu o face. Totuși, pare mai degrabă inutil, având în vedere că tipul trebuie să fie cunoscut la momentul compilării. Ce te face să treci peste new T() ?
adăugat autor cHao, sursa