Come risolvere nomi tipici generici nel compilatore?

8

Diciamo che ho un tale codice (e il suo significato è come in C #):

class Foo<T>
{
   public T my_field;
}

e più tardi nel codice che ho:

var foo = new Foo<int>(); 
foo.my_field = 5;

Il mio problema inizia con Foo<int> . Quello che faccio attualmente è prendere tipo Foo<T> e mappare T -> int e clonare la superficie di quel tipo. Superficie perché salta le espressioni di init, i corpi metodo / proprietà, ma ciononostante creo qualcosa del genere al volo:

class Foo<int>
{
   public int my_field;
}

e poi continua come sarebbe il solito tipo. La cosa buona è che funziona, ma la cosa brutta è che funziona lentamente - il profiler non esita a sottolineare che è il collo di bottiglia.

L'altra idea che ho sta usando qualcosa come una "vista" che consisterebbe nel riferimento al tipo generico (qui Foo<T> ) e alla mappatura (qui T -> int ) e quindi utilizzandolo ogni volta che faccio riferimento a un'istanza di tipo generico . In caso di foo , passerei attraverso tale vista a un campo, otterrei il suo tipo T e "filtrerò" nuovamente attraverso la vista per ottenere finalmente il risultato: int . Vorrei evitare l'approccio di prova e errore in modo ...

La mia domanda è come le istanze di tipo generico vengono gestite nel compilatore (non in runtime)? Intendo per davvero: -).

    
posta greenoldman 03.07.2016 - 15:48
fonte

1 risposta

3

Quello che ho fatto (in un compilatore che ha come target CIL) è una serie di oggetti che rappresentano i vari elementi nel sistema di tipi:

  • GenericParameter - rappresenta T nel codice sopra, in particolare T di Foo . Ha un nome e un vincolo.
  • GenericArgumentReferenceType - rappresenta T come usato nel campo sopra. Non è una dichiarazione di un tipo generico, è un riferimento a un altro.
  • BoundGenericType - rappresenta Foo<int> quando int è associato a T . Questa associazione è utile poiché puoi vedere qual è il tipo generico di origine e il tipo concreto e modellare i costrutti .NET TypeBuilderInstantiation e MakeGenericType . È una semplice coppia con qualche logica per restituire il tipo "efficace".

Quindi Foo<T> è un tipo con un GenericParameter , che ha un campo con GenericArgumentReference . Foo<int> quindi costruisce un nuovo BoundGenericType che lega Foo<T> e int . Per me il tipo generico associato non ha campi - è solo una coppia che unisce i due insieme. I devo farlo per prevenire l'esplosione dell'oscilloscopio, ma potrebbe essere specifico per la mia lingua.

I anche hanno oggetti separati per rappresentare i punti di inferenza per i generici di funzioni. E ci sono espressioni separate che accedono ai parametri generici nelle funzioni. Questi sono forse al di fuori della portata della tua domanda.

Indipendentemente da ciò, i generici tendono ad essere ingannevoli. Ricordati di allenarti:

  • Che cosa accade quando hai Bar<T> e ha un Foo<T> membro?
  • Che cosa succede quando hai una funzione in Foo<T> che ha un parametro generico T ?
  • Che cosa accade quando hai un List<T> in Foo<T> ?
  • Che cosa succede quando stai inferendo T per un Foo<T> in una funzione?
risposta data 03.07.2016 - 22:38
fonte

Leggi altre domande sui tag