Estructura, Interfaces y Boxeo

Posible duplicado:
¿Es seguro para las estructuras implementar interfaces?

Toma este código:

interface ISomeInterface { public int SomeProperty { get; } } struct SomeStruct : ISomeInterface { int someValue; public int SomeProperty { get { return someValue; } } public SomeStruct(int value) { someValue = value; } } 

y luego hago esto en alguna parte:

 ISomeInterface someVariable = new SomeStruct(2); 

es el SomeStruct en caja en este caso?

Sí lo es. Básicamente cada vez que necesita una referencia y solo tiene un valor de tipo de valor, el valor está enmarcado.

Aquí, ISomeInterface es una interfaz, que es un tipo de referencia. Por lo tanto, el valor de someVariable es siempre una referencia, por lo que el valor de la estructura recién creada debe estar encuadrado.

El punto de Jon es cierto, pero como nota al margen hay una pequeña excepción a la regla; generics. Si tiene where T : ISomeInterface , entonces esto está restringido y usa un código de operación especial . Esto significa que la interfaz se puede usar sin el boxeo. Por ejemplo:

 public static void Foo(T obj) where T : ISomeInterface { obj.Bar(); // Bar defined on ISomeInterface } 

Esto no involucra el boxeo, incluso para el tipo de valor T Sin embargo, si (en el mismo Foo ) lo haces:

 ISomeInterface asInterface = obj; asInterface.Bar(); 

luego esas cajas como antes. El restringido solo se aplica directamente a T

Estoy agregando esto para arrojar un poco más de luz sobre las respuestas ofrecidas por Jon y Marc.

Considere este método no genérico:

 public static void SetToNull(ref ISomeInterface obj) { obj = null; } 

Hmm … estableciendo un parámetro ref en nulo. Eso es solo posible para un tipo de referencia, ¿correcto? (Bueno, o para un Nullable ; pero Nullable para simplificar las cosas). Por lo tanto, el hecho de que este método se compile nos dice que una variable declarada como de algún tipo de interfaz debe tratarse como un tipo de referencia.

La frase clave aquí es “declarada como”: considere este bash de llamar al método anterior:

 var x = new SomeStruct(); // This line does not compile: // "Cannot convert from ref SomeStruct to ref ISomeInterface" -- // since x is declared to be of type SomeStruct, it cannot be passed // to a method that wants a parameter of type ref ISomeInterface. SetToNull(ref x); 

Por supuesto, la razón por la que no se puede pasar x en el código anterior a SetToNull es que x debe declararse como ISomeInterface para que pueda pasar ref x – y no porque el comstackdor sepa mágicamente que SetToNull incluye la línea obj = null . Pero de una manera que simplemente refuerza mi punto: la línea obj = null es legal precisamente porque sería ilegal pasar una variable no declarada como ISomeInterface al método.

En otras palabras, si una variable se declara como ISomeInterface , se puede establecer como null, pura y simple. Y eso se debe a que las interfaces son tipos de referencia; por lo tanto, declarar un objeto como una interfaz y asignarlo a un objeto de tipo de valor cuadra ese valor.

Ahora, por otro lado, considere este hipotético método genérico:

 // This method does not compile: // "Cannot convert null to type parameter 'T' because it could be // a non-nullable value type. Consider using 'default(T)' instead." -- // since this method could take a variable declared as, eg, a SomeStruct, // the compiler cannot assume a null assignment is legal. public static void SetToNull(ref T obj) where T : ISomeInterface { obj = null; } 

La documentación de MSDN nos dice que las estructuras son valores, no tipos de referencia. Se encuadran al convertir a / desde una variable de tipo object . Pero la pregunta central aquí es: ¿qué pasa con una variable de un tipo de interfaz? Dado que la interfaz también puede ser implementada por una clase, esto debe ser equivalente a la conversión de un valor a un tipo de referencia, como Jon Skeet ya dijo, por lo tanto, sí el boxeo podría ocurrir. Más discusión en un blog msdn .