2013-07-14 11 views
6

L'interfaccia (I) è un tipo di riferimento, struct (S) è un tipo di valore. Le strutture possono implementare interfacce.Come posso evitare il pugilato quando si passa un valore di struct come valore di interfaccia?

public interface I {} 
struct S: I {} 

assumere v'è un valore di S che viene passata ad un metodo come argomento di I. In questo caso deve essere confezionato.

void Method(I i) {} 

void Test() { 
    var s = new S(); 
    this.Method(s); // <---- boxing! 
} 

C'è un modo per evitare la boxe in questo caso?

+1

suona come una potenziale domanda intervista a ... cercherò di ricordare che uno –

risposta

14

È possibile evitare la boxe se si modifica la definizione di Method a:

void Method<T>(T i) where T : I 
{ 
} 

Questo evita la boxe, perché in fase di esecuzione il CLR è specializzata metodi generici in base al tipo di argomento generico (s). I tipi di riferimento possono tutti condividere la stessa implementazione, mentre i tipi di struct hanno ciascuno la propria versione. Ciò significa che tutte le operazioni in Method che dipendono da T terranno conto della dimensione del tipo di struttura in calcestruzzo.

È necessario fare attenzione però, perché richiamare metodi virtuali definiti su System.Object come Equals o GetHashCode causerà i essere inscatolato dal metodo di spedizione virtuale richiede un puntatore tavolo metodo (anche se la squadra può essere in grado di fare l'invio staticamente in qualche casi). Tuttavia, se il tipo di struct sovrascrive il metodo (i) virtuale in questione, non sarà necessario eseguire la boxe, poiché il metodo da chiamare è nuovamente noto staticamente poiché le strutture (e quindi i loro membri) sono sigillate.

Di solito è possibile evitare di chiamare Equals o GetHashCode direttamente vincolando T ulteriormente implementare IEquatable<T> e utilizzando un IEqualityComparer<T> per i confronti per esempio

void Method<T>(T i) where T : I, IEquatable<T> 
{ 
    T other = ... 
    if(i.Equals(other)) //avoids boxing 
    { 
    } 
} 
+0

si può spiegare in modo esplicito il motivo per cui questo funziona? –

+0

Hai dimenticato il vincolo della struttura? dove T: struct, I –

+0

@BradRem: Se conosci il tuo tipo in fase di compilazione quando lo passi, l'inferenza di tipo C# prenderà il via e sarà come chiamare 'Metodo (s);'. A questo punto è riconosciuto che stai usando una struttura ed eviterete la boxe. EDIT: Tuttavia, se non conosci il tipo di 's' in fase di compilazione (cioè, lo hai digitato solo come interfaccia' I' diciamo scrivendo: 'I s = new S()') allora sarà ancora essere in scatola. (ovviamente, a quel punto è _already_ boxed quindi non molto altro da fare) –

0

Utilizzare farmaci generici:

public interface I {} 
pubic struct S : I {} 
public class Foo 
{ 
    public static void Bar<T>(T i) 
     where T : I 
    {} 
} 
Problemi correlati