¿Por qué no puedo usar la palabra clave as para una estructura?

Definí la siguiente estructura:

public struct Call { public SourceFile caller; public SourceFile callee; public Call(SourceFile caller, SourceFile callee) { this.caller = caller; this.callee = callee; } } 

Más tarde, lo asigno a la propiedad Tag de otro objeto:

 line.Tag = new Call(sf1, sf2); 

Pero cuando bash recuperar la propiedad Tag como tal,

 Call call = line.Tag as Call; 

Visual Studio da el siguiente error en tiempo de comstackción:

El operador debe utilizarse dentro de un tipo de referencia o un tipo anulable

¿Cuál es el significado de eso? ¿Y cómo puedo solucionarlo?

Una estructura es un tipo de valor, por lo que no se puede utilizar con el operador as . El operador as debe poder asignar un valor nulo si la conversión falla. Esto solo es posible con un tipo de referencia o un tipo de valor anulable.

Hay un par de maneras de resolver esto, pero lo mejor es cambiar el tipo de Call de una estructura a una clase. Básicamente, esto cambiará su tipo de un tipo de valor a un tipo de referencia, lo que le permite al operador as asignarle un valor nulo si la conversión falla.

Para obtener más información sobre los tipos de valor en comparación con los tipos de referencia, este es un artículo decente. Además, echa un vistazo a MSDN:

  • tipos de valor
  • tipos de referencia
  • como operador
  • tipos anulables

Algunas de las respuestas existentes no son del todo correctas. No puede usar tipos no anulables con as , porque el resultado de as es el valor nulo del tipo si el primer operando no es realmente del tipo adecuado.

Sin embargo, puede usar as con los tipos de valor … si son anulables:

 int a = 10; object o = a; int? x = o as int?; // x is a Nullable with value 10 long? y = o as long?; // y is a Nullable with the null value 

Para que puedas usar:

 Call? call = line.Tag as Call?; 

Entonces puedes usarlo como:

 if (call != null) { // Do stuff with call.Value } 

Dos advertencias sin embargo:

  • En mi experiencia, esto es más lento que solo usarlo is seguido por un lanzamiento
  • Deberías reconsiderar seriamente tu tipo de Call actual:
    • Está exponiendo campos públicos, lo que generalmente es una mala encapsulación.
    • Es un tipo de valor mutable, que es casi seguramente un error.

Le sugiero encarecidamente que lo convierta en una clase en su lugar, momento en el que este problema desaparece de todos modos.

Otro pensamiento: si la etiqueta siempre debería ser una Call , entonces es mejor lanzarla:

 Call call = (Call) line.Tag; 

De esa manera, si los datos no coinciden con sus expectativas (es decir, hay algún error que indica que la Tag no es una Call ), podrá descubrirlo pronto, en lugar de hacerlo después de haber realizado algún otro trabajo. Tenga en cuenta que esta conversión se comportará de manera diferente dependiendo de si la Call es una estructura o una clase, si la Tag es nula – puede convertir un valor nulo a una variable de un tipo de referencia (o un tipo de valor que acepta valores nulos), pero no a un no tipo de valor anulable.

De la especificación de C #

§7.10.11 El operador as se utiliza para convertir explícitamente un valor a un tipo de referencia o tipo anulable dado. A diferencia de una expresión de conversión (§7.7.6), el operador as nunca lanza una excepción. En cambio, si la conversión indicada no es posible, el valor resultante es nulo .

Las referencias y los tipos anulables pueden ser nulos. Los stucts son tipos de valor por lo que no pueden ser nulos.

 Call? call = line.Tag as Call?; 

Es una limitación de C #. Si el tipo fuera un tipo de referencia, entonces si la conversión fallara, simplemente devolvería ‘nulo’, pero como es un tipo de valor, no sabe qué devolver cuando falla la conversión.

Debe reemplazar su uso de como con dos: ‘es’ y ‘como’

 if (line.Tag is Call) { call = (Call)line.Tag; } else { // Do whatever you would do if as returned null. } 

¿Cuál es el significado? Como se dijo, las estructuras son tipos de valor.

¿Cómo puedo resolverlo?

 Call call = line.Tag;