En .NET Si pasa una estructura a un método con un parámetro de interfaz, ¿encerra el valor?
Pregunta
Desde una prueba simple, puedo ver que si pasa la estructura al método, se pasa por valor, pero si primero lo asigna a una interfaz, se pasa por referencia.
interface IFoo { int Val { get; set; } }
struct Foo : IFoo { public int Val { get; set; } }
void Bar(IFoo foo) { foo.Val = 1; }
Foo foo = new Foo();
IFoo ifoo = new Foo();
Bar(foo);
Bar(ifoo);
Console.WriteLine(foo.Val); // 0, passed by value
Console.WriteLine(ifoo.Val); // 1, passed by ref
Entonces, mi pregunta es, ¿todavía hay una operación de boxeo para pasar una estructura como esta?
Solución
Cada vez que una estructura se convierte en una interfaz, se encaja.
Foo foo = new Foo();//Doesn't box yet
IFoo ifoo = new Foo();//Boxes
Bar(foo);//creates a new boxed copy
Bar(ifoo);//Uses the old boxed version created in line 2
Puede evitar ese boxeo haciendo el parámetro genérico utilizando la interfaz como una restricción:
void Bar<T>(T foo)
where T:IFoo
{
}
Esto utiliza el hecho de que los genéricos se especializan para cada tipo de valor.
Pero si sigue la directriz de diseño de que las estructuras mutables son malvadas y, por lo tanto, hacen que su estructura sea inmutable, no importa mucho si el código se cuadra o no. El boxeo solo causa un ligero golpe de rendimiento, pero no cambia mucho la semántica.
Otros consejos
Las interfaces son todos los tipos de referencia, por lo que el boxeo ocurre en la línea
IFoo ifoo = new Foo();
en lugar de cuando llame Bar
.