Pregunta

Considere 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 (bueno, el tercero no es un casting, pero obtienes la intención). ¿Cuál debería ser preferido?

¿Fue útil?

Solución

string s = (string)o; // 1

Lanza InvalidCastException si o es no una cadena . 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 cadena o si o es nulo . Por este motivo, 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 nulo . Asigna cualquier o.ToString () devuelve a s , sin importar de qué tipo sea o .


Use 1 para la mayoría de las conversiones, es simple y directo. Tiendo a usar casi nunca 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 una conversión y es solo una invocación de método. Úselo para cuando necesite la representación de cadena de un objeto que no sea de cadena.

Otros consejos

  1. string s = (string) o; Se usa cuando algo debería definitivamente sea la otra cosa.
  2. string s = o as string; Se usa cuando algo podría ser el otro cosa.
  3. string s = o.ToString (); Se usa cuando no te importa qué es pero solo quieres usar el Representación de cadena disponible.

Realmente depende de si sabes si o es una cadena y qué quieres hacer con ella. Si su comentario significa que o realmente es una cadena, preferiría la conversión directa de (cadena) o - es poco probable que falle.

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

Con el operador como , 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 más adelante y tendrá una NullReferenceException arrojado. Estos tienden a ser más comunes y un lote es más difícil de rastrear una vez que ocurren en la naturaleza, ya que casi todas las líneas hacen referencia a una variable y pueden lanzar una. Por otro lado, si está tratando de convertir a un tipo de valor (cualquier primitivo o estructuras como DateTime ), tiene que usar el modelo directo: el as no funcionará.

En el caso especial de convertir 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 El método ToString puede hacer lo que quieras.

Si ya sabe a qué tipo de fuente se puede convertir, use una conversión de estilo C:

var o = (string) iKnowThisIsAString; 

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

Si no sabes si es el tipo deseado y lo vas a usar si es así, usa la palabra clave as :

var s = o as string;
if (s != null) return s.Replace("_","-");

//or for early return:
if (s==null) return;

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

Use ToString () para obtener una representación de cadena legible para cualquier objeto, incluso si no se puede convertir a cadena.

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

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

Esto significa que puedes operar con la variable escrita en lugar de tener que lanzarla desde objeto como lo harías con una conversión directa:

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

No es una gran cosa, pero guarda líneas de código y asignación de variables, además de que es más legible

'as' se basa en 'is', que es una palabra clave que verifica en tiempo de ejecución si el objeto es polimórficamente compatible (básicamente si se puede realizar una conversión) y devuelve nulo si la verificación falla.

Estos dos son equivalentes:

Usando 'como':

string s = o as string;

Usando 'is':

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

Por el contrario, el reparto de estilo c se realiza también en tiempo de ejecución, pero lanza una excepción si no se puede realizar el reparto.

Solo para agregar un dato importante:

La palabra clave 'como' solo funciona con tipos de referencia. No puedes hacer:

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

En esos casos tienes que usar casting.

2 es útil para convertir 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 alimentado con un mínimo de lanzamientos.

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

(esta página está teniendo algunos errores de " referencia ilegal " aparecen a veces, así que simplemente actualice si lo hace)

La conclusión es, la " como " El operador es normalmente más rápido que un yeso. A veces, muchas veces más rápido, a veces apenas más rápido.

Peronsonally thing " como " También es más legible.

Entonces, ya que es más rápido y más seguro " " (no lanzar excepción), y posiblemente más fácil de leer, recomiendo usar " como " todo el tiempo.

" (cadena) o " dará como resultado una excepción InvalidCastException ya que no hay un lanzamiento directo.

" o como cadena " resultará en que s sea una referencia nula, en lugar de una excepción lanzada.

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

No olvide que para convertir en cadena, también hay Convert.ToString (someType instanceOfThatType) donde someType es uno de un conjunto de tipos, esencialmente los tipos básicos de marcos.

Todas las respuestas dadas son buenas, si puedo agregar algo: Para usar directamente los métodos y las propiedades de la cadena (por ejemplo, ToLower) no puede 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

Se prefiere, ya que evita la penalización de rendimiento de doble conversión.

Parece que los dos son conceptualmente diferentes.

Lanzamiento directo

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

  • Conversión implícita / explícita personalizada: generalmente se crea un nuevo objeto.
  • Tipo de valor implícito: copie 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, se lanzará una excepción.
  • Mismo tipo: "La conversión es redundante '.

Parece que el objeto se convertirá 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 los mismos, solo cambian las referencias.
  • Tipos de valor: Copiar tipos de boxeo y anulables.

Parece que vas 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<int32>::.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
    }

Me gustaría llamar la atención sobre los siguientes detalles específicos del operador as :

https://docs.microsoft.com / en-us / dotnet / csharp / language-reference / keywords / as

  

Tenga en cuenta que el operador as solo realiza conversiones de referencia,   conversiones anulables, y las conversiones de boxeo. El operador no puede   realizar otras conversiones, como las conversiones definidas por el usuario, que   en su lugar, debe realizarse utilizando expresiones de conversión.

Al intentar obtener la representación de cadena de cualquier cosa (de cualquier tipo) que pueda ser nula, prefiero la línea de código que se encuentra a continuación. Es compacto, invoca a 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 a Java por palabra clave es este:

obj.GetType().IsInstanceOfType(otherObj)

Utilice la cadena de conversión directa s = (cadena) o; si en el contexto lógico de su aplicación cadena es el único tipo válido. Con este enfoque, obtendrá InvalidCastException e implementará el principio de Fail- rápido . Su lógica estará protegida de pasar más allá del tipo no válido u obtener NullReferenceException si se usa como operador .

Si la lógica espera que varios tipos diferentes emitan string s = o as string; y verifíquelo en null o use es operador. / p>

En C # 7.0 ha aparecido una nueva y atractiva función para simplificar el lanzamiento y comprobar que es un Coincidencia de patrones :

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) se admiten en C #:

|

(C) v

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

• Solo es posible si el tipo dinámico de v es c, o un subtipo de c

• Si no, se lanza una excepción 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 dinámico de v no es c, o un subtipo de c

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top