2010-06-13 22 views
33

Duplicar posibles:
Is it safe for structs to implement interfaces?Las estructuras, interfaces y boxeo

Tome 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?

Respuesta

45

Sí, lo es. Básicamente siempre que necesite una referencia y solo tenga 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 estructura recién creado debe estar encuadrado.

+0

que supone que. No estoy del todo seguro de qué fue lo que me hizo dudar de que ese sería el caso. Solo pensé en tirarlo aquí por si acaso alguien más se preguntaba algo. – Sekhat

+2

Dale a un hombre una herramienta para obtener respuestas (Red Gate Reflector), y él tendrá respuestas de por vida. Pero dale solo una respuesta y él volverá con más preguntas y más puntos de recomendación SO ... –

+18

@Ben: Por otro lado, dale a un hombre una herramienta y ellos tendrán que verificarlo cada vez que lo hagan '. no estoy seguro Dale a un hombre una * explicación * y ellos podrán razonar sobre esto por sí mismos. –

0

El MSDN documentation nos dice que las estructuras son valiosas, no son tipos de referencia. Se empaquetan al convertir a/desde una variable del 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. More discussion on an msdn blog.

+0

La manera más simple de pensar sobre este tema es reconocer que cada variable, parámetro o campo debe tener algún tipo de asignación concreta además de una combinación de interfaces (posiblemente vacía). Si no hay otro tipo concreto disponible, el sistema asumirá una referencia de objeto. – supercat

54

El punto de Jon es cierto, pero como nota al margen hay una pequeña excepción a la regla; genéricos. Si tiene where T : ISomeInterface, entonces este es constreñido, y usa un special opcode. Esto significa que la interfaz se puede usar sin boxeo. Por ejemplo:

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

Esto no implican el boxeo, incluso para el valor de tipo T. Sin embargo, si (en la misma Foo) que hace:

ISomeInterface asInterface = obj; 
asInterface.Bar(); 

entonces que las cajas que antes. El constreñidosolo se aplica directamente al T.

+0

hai, no será empaquetado porque el método llamado después de que se resuelvan todos los genéricos es 'void Foo (SomeStruct obj) 'no' void Foo (ISomeInterface obj) ' – Sekhat

+1

@Sekhat: los parámetros genéricos se resuelven en tiempo de ejecución para que el compilador no sabe que se llama al método con un tipo de valor. – adrianm

+1

@Sekhat - para expandir el punto de @ adrianm: el mismo IL se usa para todos los llamantes. Cada parámetro de tipo de valor obtiene JITted por separado, pero todos los ref-types comparten un JIT. El compilador no tiene ** nada ** que ver con esto; Los genéricos de .NET son tiempo de ejecución, no de tiempo de compilación. La firma es Foo (T obj) en todos los casos. –

8

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

consideran este método no genérico:

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

Hmm ... establecer un parámetro ref en nulo. Eso es solo posible para un tipo de referencia, ¿correcto? (Bueno, o para un Nullable<T>; pero ignoremos ese caso para mantener las cosas simples). 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 "declarado": considerar este intento 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 para SetToNull es que haría x debe declararse como ISomeInterface para que pueda pasar ref x - y no porque el compilador sabe mágicamente que SetToNull incluye la línea obj = null. Pero de una manera que sólo refuerza mi punto: la línea obj = null es legal precisamente porque sería ilegal pasar una variable no declarado como un ISomeInterface al método.

En otras palabras, si una variable se declara como ISomeInterface, se puede establecer como nula, 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 el contrario, consideran que este método genérico hipotético:

// 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, e.g., a SomeStruct, 
// the compiler cannot assume a null assignment is legal. 
public static void SetToNull<T>(ref T obj) where T : ISomeInterface { 
    obj = null; 
} 
+1

Esto no tiene nada que ver con los tipos de valores y tipos de referencia, y todo que ver con la varianza. –

+4

@Ben: supongo que estás diciendo eso por mi ejemplo 'ref', que dudé incluir porque pensé que podría ser un poco confuso. Pero mi punto es que si una variable se declara como 'ISomeInterface', se puede establecer en null, que solo es cierto para un tipo de referencia. Por lo tanto, establecer una variable 'ISomeInterface' en un objeto de tipo de valor incurre en boxeo. Esto tiene mucho que ver con los tipos de valores y tipos de referencia. Si una variable se declara como un tipo de valor particular, esa variable * no puede * establecerse como nula. –