¿Operador de conversión directa frente a ‘como’?

Considera el siguiente código:

void Handler(object o, EventArgs e) { // I swear o is a string string s = (string)o; // 1 //-OR- string s = o as string; // 2 // -OR- string s = o.ToString(); // 3 } 

Cuál es la diferencia entre los tres tipos de casting (está bien, el tercero no es un casting, pero obtienes la intención). ¿Cuál debería ser el preferido?

 string s = (string)o; // 1 

Lanza InvalidCastException si o no es una string . De lo contrario, asigna o a s , incluso si o es null .

 string s = o as string; // 2 

Asigna null a s si o no es una string o si o es null . Por esta razón, no puede usarlo con tipos de valor (el operador nunca podría devolver null en ese caso). De lo contrario, asigna o a s .

 string s = o.ToString(); // 3 

Causa una NullReferenceException si o es null . Asigna cualquier cosa que o.ToString() regrese a s , sin importar qué tipo o sea.


Use 1 para la mayoría de las conversiones, es simple y directo. Tiendo a casi nunca usar 2 ya que si algo no es del tipo correcto, generalmente espero que ocurra una excepción. Solo he visto la necesidad de este tipo de funcionalidad de retorno nulo con bibliotecas mal diseñadas que usan códigos de error (por ejemplo, return null = error, en lugar de usar excepciones).

3 no es un elenco y solo es una invocación a un método. Úselo para cuando necesite la representación de cadena de un objeto que no sea de cuerda.

  1. Úselo cuando algo debería ser definitivamente la otra cosa.
  2. Úselo cuando algo pueda ser lo otro.
  3. Úselo cuando no le importe lo que sea, pero solo quiera usar la representación de cadena disponible.

Realmente depende de si sabes si o es una cadena y qué quieres hacer con ella. Si tu comentario significa que o realmente es una cadena, preferiría el molde directo (string)o moldeado: es poco probable que falle.

La mayor ventaja de utilizar el reparto directo es que cuando falla, obtienes una InvalidCastException , que te dice prácticamente todo lo que salió mal.

Con el operador as , si o no es una cadena, s se establece en null , lo cual es útil si no está seguro y desea probar s :

 string s = o as string; if ( s == null ) { // well that's not good! gotoPlanB(); } 

Sin embargo, si no realiza esa prueba, usará s después y tendrá una NullReferenceException lanzada. Estos tienden a ser más comunes y mucho más difíciles de rastrear una vez que ocurren en la naturaleza, ya que casi cada línea desreferencia una variable y puede arrojar una. Por otro lado, si intentas convertir a un tipo de valor (cualquier primitiva, o estructuras como DateTime ), tienes que usar el reparto directo; el as no funcionará.

En el caso especial de la conversión a una cadena, cada objeto tiene un ToString , por lo que su tercer método puede estar bien si o no es nulo y cree que el método ToString puede hacer lo que desee.

Si ya sabes a qué tipo puede enviar, usa un molde de estilo C:

 var o = (string) iKnowThisIsAString; 

Tenga en cuenta que solo con un molde de estilo C puede realizar coerción de tipo explícita.

Si no sabe si es el tipo deseado y lo va a usar si lo está, use como palabra clave:

 var s = o as string; if (s != null) return s.Replace("_","-"); //or for early return: if (s==null) return; 

Tenga en cuenta que no llamará a ningún operador de conversión de tipo. Solo será no nulo si el objeto no es nulo y nativo del tipo especificado.

Utilice ToString () para obtener una representación en cadena legible para el ser humano de cualquier objeto, incluso si no se puede convertir en una cadena.

La palabra clave as es buena en asp.net cuando usa el método FindControl.

 Hyperlink link = this.FindControl("linkid") as Hyperlink; if (link != null) { ... } 

Esto significa que puede operar en la variable tipeada en lugar de tener que lanzarla desde un object como lo haría con un lanzamiento directo:

 object linkObj = this.FindControl("linkid"); if (link != null) { Hyperlink link = (Hyperlink)linkObj; } 

No es algo enorme, pero ahorra líneas de código y asignación de variables, además es más legible

‘as’ se basa en ‘es’, que es una palabra clave que verifica en tiempo de ejecución si el objeto es compatible polimórficamente (básicamente, si se puede hacer un molde) y devuelve nulo si falla la comprobación.

Estos dos son equivalentes:

Usando ‘como’:

 string s = o as string; 

Usando ‘es’:

 if(o is string) s = o; else s = null; 

Por el contrario, el elenco de estilo c se hace también en tiempo de ejecución, pero arroja una excepción si no se puede hacer el reparto.

Solo para agregar un hecho importante:

La palabra clave ‘como’ solo funciona con tipos de referencia. Tú no puedes hacer:

 // I swear i is an int int number = i as int; 

En esos casos, debes usar el casting.

2 es útil para lanzar a un tipo derivado.

Supongamos que a es un Animal:

 b = a as Badger; c = a as Cow; if (b != null) b.EatSnails(); else if (c != null) c.EatGrass(); 

obtendrá un fed con un mínimo de moldes.

“(cadena) o” dará como resultado una InvalidCastException ya que no hay conversión directa.

“o como cadena” dará como resultado que s sea una referencia nula, en lugar de una excepción lanzada.

“o.ToString ()” no es un elenco de ningún tipo per-se, es un método que se implementa por objeto, y por lo tanto de una manera u otra, por cada clase en .net que “hace algo” con la instancia de la clase a la que se llama y devuelve una cadena.

No olvide que para convertir a cadena, también hay Convert.ToString (someType instanceOfThatType) donde someType es uno de un conjunto de tipos, esencialmente los tipos base de frameworks.

Según los experimentos que se ejecutan en esta página: http://www.dotnetguru2.org/sebastienros/index.php/2006/02/24/cast_vs_as

(esta página presenta algunos errores de “referencia ilegal” a veces, así que simplemente actualice si lo hace)

La conclusión es que el operador “como” es normalmente más rápido que un elenco. A veces, muchas veces más rápido, a veces apenas más rápido.

Yo peronsonally cosa “como” también es más legible.

Entonces, como es más rápido y “más seguro” (no arrojará excepciones), y posiblemente sea más fácil de leer, recomiendo usar “como” todo el tiempo.

Todas las respuestas dadas son buenas, si pudiera agregar algo: para usar directamente los métodos y propiedades de la cadena (por ejemplo, ToLower) no puedes escribir:

 (string)o.ToLower(); // won't compile 

solo puedes escribir:

 ((string)o).ToLower(); 

pero podrías escribir en su lugar:

 (o as string).ToLower(); 

La opción as es más legible (al menos en mi opinión).

 string s = o as string; // 2 

Es preferible, ya que evita la penalización de rendimiento del doble lanzamiento.

Parece que los dos son conceptualmente diferentes.

Casting directo

Los tipos no tienen que estar estrictamente relacionados. Viene en todo tipo de sabores.

  • Fundición implícita / explícita personalizada: generalmente se crea un objeto nuevo.
  • Tipo de valor implícito: copia sin perder información.
  • Tipo de valor explícito: la copia y la información pueden perderse.
  • Relación IS-A: cambie el tipo de referencia, de lo contrario arroja una excepción.
  • Mismo tipo: ‘Casting es redundante’.

Se siente como si el objeto se convirtiera en otra cosa.

Operador AS

Los tipos tienen una relación directa. Como en:

  • Tipos de referencia: relación IS-A Los objetos son siempre iguales, solo los cambios de referencia.
  • Tipos de valores: copie el boxeo y los tipos anulables.

Se siente como si fuera a manejar el objeto de una manera diferente.

Muestras e IL

  class TypeA { public int value; } class TypeB { public int number; public static explicit operator TypeB(TypeA v) { return new TypeB() { number = v.value }; } } class TypeC : TypeB { } interface IFoo { } class TypeD : TypeA, IFoo { } void Run() { TypeA customTypeA = new TypeD() { value = 10 }; long longValue = long.MaxValue; int intValue = int.MaxValue; // Casting TypeB typeB = (TypeB)customTypeA; // custom explicit casting -- IL: call class ConsoleApp1.Program/TypeB ConsoleApp1.Program/TypeB::op_Explicit(class ConsoleApp1.Program/TypeA) IFoo foo = (IFoo)customTypeA; // is-a reference -- IL: castclass ConsoleApp1.Program/IFoo int loseValue = (int)longValue; // explicit -- IL: conv.i4 long dontLose = intValue; // implict -- IL: conv.i8 // AS int? wraps = intValue as int?; // nullable wrapper -- IL: call instance void valuetype [System.Runtime]System.Nullable`1::.ctor(!0) object o1 = intValue as object; // box -- IL: box [System.Runtime]System.Int32 TypeD d1 = customTypeA as TypeD; // reference conversion -- IL: isinst ConsoleApp1.Program/TypeD IFoo f1 = customTypeA as IFoo; // reference conversion -- IL: isinst ConsoleApp1.Program/IFoo //TypeC d = customTypeA as TypeC; // wouldn't compile } 

Al tratar de obtener la representación de cadena de cualquier cosa (de cualquier tipo) que podría ser nula, prefiero la siguiente línea de código. Es compacto, invoca ToString () y maneja correctamente los nulos. Si o es nulo, s contendrá String.Empty.

 String s = String.Concat(o); 

Como nadie lo mencionó, el más cercano a instanceOf to Java por palabra clave es este:

 obj.GetType().IsInstanceOfType(otherObj) 

Use string s = (string) o; lanzamiento directo string s = (string) o; si en el contexto lógico de la aplicación, la string es el único tipo válido. Con este enfoque, obtendrá InvalidCastException e implementará el principio de Fail-fast . Su lógica estará protegida para no pasar más el tipo inválido u obtener NullReferenceException si se usa as operador.

Si la lógica espera que varios tipos diferentes emitan string s = o as string; y verificarlo en null o usar is operador.

Nueva característica interesante ha aparecido en C # 7.0 para simplificar el lanzamiento y el control es una coincidencia de patrón :

 if(o is string s) { // Use string variable s } or switch (o) { case int i: // Use int variable i break; case string s: // Use string variable s break; } 

Las siguientes dos formas de conversión de tipo (conversión) son compatibles con C #:

|

(CV

• Convierta el tipo estático de v a c en la expresión dada

• Solo es posible si el tipo dynamic de v es c, o un subtipo de c

• Si no, se lanza una InvalidCastException

|

v como C

• Variante no fatal de (c) v

• Por lo tanto, convierta el tipo estático de v a c en la expresión dada

• Devuelve nulo si el tipo dynamic de v no es c, o un subtipo de c