c# - safe - Casting directo vs ''como'' operador?
casting de objetos en c# (17)
- Usar cuando algo debería ser definitivamente la otra cosa.
- Utilizar cuando algo podría ser la otra cosa.
- Úselo cuando no le importa lo que sea, pero solo desea usar la representación de cadena disponible.
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 la preferida?
"(cadena) o" dará como resultado una excepción InvalidCastException ya que no hay conversión directa.
"o como cadena" dará como resultado que s sea una referencia nula, en lugar de lanzar una excepción.
"o.ToString ()" no es una conversión de ningún tipo per se, 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.
''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 ''es'':
if(o is string)
s = o;
else
s = null;
Por el contrario, el reparto de estilo C también se realiza en tiempo de ejecución, pero lanza una excepción si no se puede realizar 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 tienes que usar 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á una alimentación con un mínimo de lanzamientos.
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)
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 puede operar con la variable escrita en lugar de tener que lanzarla desde un object
como lo haría 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 es más legible
Las siguientes dos formas de conversión de tipo (conversión) se admiten en C #:
|
(CV
• Convertir 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
Me gustaría llamar la atención sobre las siguientes especificaciones 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 que pueden contener nulos y conversiones de boxeo. El operador as no puede realizar otras conversiones, como las conversiones definidas por el usuario, que deberían realizarse utilizando expresiones de conversión.
Parece que los dos son conceptualmente diferentes.
Casting directo
Los tipos no tienen que estar estrictamente relacionados. Se presenta en todo tipo de sabores.
- Conversión implícita / explícita personalizada: normalmente se crea un nuevo objeto.
- Tipo de valor implícito: Copie sin perder información.
- Tipo de valor explícito: se puede perder la copia y la información.
- Relación IS-A: Cambia el tipo de referencia, de lo contrario lanza una excepción.
- Mismo tipo: ''Casting es redundante''.
Se siente como si el objeto se va a 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 cambia la referencia.
- Tipos de valor: Copia de boxeo y tipos anulables.
Se siente como 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
}
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 ola recta (string)o
, es poco probable que falle.
La mayor ventaja de usar el cast directo es que cuando falla, obtienes una InvalidCastException , que te dice qué fue lo que salió mal.
Con el operador as
, si o
no es una cadena, s
se establece en null
, lo que 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 tarde y se NullReferenceException una NullReferenceException . 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 todas las líneas hacen referencia a una variable y pueden lanzar una. Por otro lado, si está intentando convertir a un tipo de valor (cualquier primitivo, o estructuras como DateTime ), tiene que usar la conversión directa, as
que no funcionará.
En el caso especial de convertir a una cadena, cada objeto tiene una 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 quiera.
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 tiene algunos errores de "referencia ilegal" que se muestran a veces, así que simplemente actualice si lo hace)
La conclusión es que el operador "as" es normalmente más rápido que un reparto. A veces, muchas veces más rápido, a veces apenas más rápido.
Por lo general, la cosa "como" también es más legible.
Por lo tanto, dado que es más rápido y "más seguro" (no arrojar excepciones), y posiblemente más fácil de leer, recomiendo usar "como" todo el tiempo.
Si ya sabe a qué tipo se puede convertir, use un modelo de estilo C:
var o = (string) iKnowThisIsAString;
Tenga en cuenta que solo con una conversión de estilo C se puede realizar una coacción de tipo explícita.
Si no sabe si es el tipo deseado y lo usará si lo es, 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 como no llamará a ningún tipo de operadores de conversión. Solo será no nulo si el objeto no es nulo y de forma nativa del tipo especificado.
Use ToString () para obtener una representación de cadena legible para cualquier objeto, incluso si no se puede convertir en cadena.
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).
Utilice la string s = (string) o;
conversión directa string s = (string) o;
si en el contexto lógico de su string
aplicación es el único tipo válido. Con este enfoque, obtendrá la InvalidCastException
e implementará el principio de Fail-fast . Su lógica estará protegida de pasar más allá del tipo no válido u obtener NullReferenceException si se usa as
operador.
Si la lógica espera que varios tipos diferentes emitan una string s = o as string;
Y comprobarlo en null
o usarlo is
operador.
Una nueva característica interesante ha aparecido en C # 7.0 para simplificar el lanzamiento y verificar que sea una 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;
}
string s = (string)o; // 1
Lanza la 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 este motivo, no puede usarlo con tipos de valor (el operador nunca podría devolver un null
en ese caso). De lo contrario, asigna o
a s
.
string s = o.ToString(); // 3
Provoca una NullReferenceException si o
es null
. Asigna lo que o.ToString()
devuelve a s
, sin importar qué tipo o
es.
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 lanzamiento y es solo una invocación de método. Úselo para cuando necesite la representación de cadena de un objeto no de cadena.
string s = o as string; // 2
Se prefiere, ya que evita la penalización de rendimiento de doble casting.