2013-07-14 9 views
6

Interfejs (I) jest typem odniesienia, struct (S) jest typem wartości. Struktury mogą implementować interfejsy.Jak mogę uniknąć boksowania podczas przekazywania wartości struct jako wartości interfejsu?

public interface I {} 
struct S: I {} 

Załóżmy, że istnieje wartość S, która jest przekazywana do metody jako argument I. W tym przypadku musi być w pudełku.

void Method(I i) {} 

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

Czy istnieje sposób na uniknięcie boksowania w tym przypadku?

+1

brzmi jak potencjalnego pytanie wywiad do ... Postaram się pamiętać, że jedna –

Odpowiedz

14

Można uniknąć boks po zmianie definicji Method do:

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

ten sposób unika się boks, ponieważ w czasie wykonywania CLR specjalizuje metody rodzajowe w zależności od typu rodzajowego argumentu (ów). Typy referencyjne mogą mieć tę samą implementację, podczas gdy typy struktur mają własną wersję. Oznacza to, że wszystkie operacje w Method, które zależą od T, będą uwzględniać rozmiar konkretnego typu struktury.

Musisz uważać jednak, ponieważ wywoływanie metod wirtualnych zdefiniowane na System.Object jak Equals lub GetHashCode spowoduje i być zapakowane ponieważ metoda wirtualnego wysłania wymaga wskaźnik metoda tabeli (choć JIT może być w stanie zrobić wysyłkę statycznie w niektórych sprawy). Jeśli jednak twój typ struktury zastępuje metodę (metody) wirtualne, wówczas nie będzie trzeba wykonywać boksowania, ponieważ metoda wywoływania jest ponownie znana statycznie, ponieważ konstrukcje (a tym samym ich członkowie) są zapieczętowane.

Zazwyczaj można uniknąć wywoływania Equals lub GetHashCode bezpośrednio poprzez ograniczenie T dalsze wdrożenie IEquatable<T> i używając porównań np IEqualityComparer<T>

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

można jednoznacznie wyjaśnić, dlaczego to działa? –

+0

Czy zapomniałeś o ograniczeniu struktury? gdzie T: struct, I –

+0

@BradRem: Jeśli znasz swój typ w czasie kompilacji, kiedy go przekażesz, wywoła się podpowiedź typu C# i będzie to wyglądało jak wywołanie 'Method (s);'. W tym momencie wiadomo, że używasz struct i unikniesz boksowania. EDYCJA: Jednakże, jeśli nie znasz typu 's' w czasie kompilacji (tzn. Masz to wpisane tylko jako interfejs' I' say pisząc: 'I s = new S()'), to nadal będzie być zapakowany. (oczywiście, w tym momencie jest on już zapakowany, więc naprawdę niewiele do zrobienia) –

0

Stosować rodzajowych:

public interface I {} 
pubic struct S : I {} 
public class Foo 
{ 
    public static void Bar<T>(T i) 
     where T : I 
    {} 
} 
Powiązane problemy