Al programar interfaces, descubrí que estoy haciendo muchas conversiones o conversión de tipo de objeto.
¿Hay alguna diferencia entre estos dos métodos de conversión? Si es así, ¿hay una diferencia de costo o cómo afecta esto a mi programa?
public interface IMyInterface
{
void AMethod();
}
public class MyClass : IMyInterface
{
public void AMethod()
{
//Do work
}
// Other helper methods....
}
public class Implementation
{
IMyInterface _MyObj;
MyClass _myCls1;
MyClass _myCls2;
public Implementation()
{
_MyObj = new MyClass();
// What is the difference here:
_myCls1 = (MyClass)_MyObj;
_myCls2 = (_MyObj as MyClass);
}
}
Además, ¿cuál es "en general" el método preferido?
Respuestas:
La respuesta debajo de la línea fue escrita en 2008.
C # 7 introdujo la coincidencia de patrones, que ha reemplazado en gran medida al
as
operador, como ahora puede escribir:Tenga en cuenta que
tt
todavía está dentro del alcance después de esto, pero no está definitivamente asignado. ( Definitivamente está asignado dentro delif
cuerpo). Eso es un poco molesto en algunos casos, por lo que si realmente te importa introducir la menor cantidad de variables posibles en cada ámbito, es posible que aún quieras usaris
seguido de un reparto.No creo que ninguna de las respuestas hasta ahora (en el momento de comenzar esta respuesta) haya explicado realmente dónde vale la pena usar cuál.
No hagas esto:
Esto no solo está comprobando dos veces, sino que puede estar comprobando cosas diferentes, si
randomObject
es un campo en lugar de una variable local. Es posible que el "si" pase pero luego el lanzamiento falle, si otro hilo cambia el valor derandomObject
entre los dos.Si
randomObject
realmente debería ser una instancia deTargetType
, es decir, si no lo es, eso significa que hay un error, entonces lanzar es la solución correcta. Eso arroja una excepción de inmediato, lo que significa que no se realiza más trabajo bajo suposiciones incorrectas, y la excepción muestra correctamente el tipo de error.Si
randomObject
podría ser una instancia deTargetType
yTargetType
es un tipo de referencia, utilice un código como este:Si
randomObject
podría ser una instancia deTargetType
yTargetType
es un tipo de valor, entonces no podemos usarloas
consigoTargetType
mismo, pero podemos usar un tipo anulable:(Nota: actualmente esto es en realidad más lento que el reparto + . Creo que es más elegante y consistente, pero ahí vamos).
Si realmente no necesita el valor convertido, pero solo necesita saber si es una instancia de TargetType, entonces el
is
operador es su amigo. En este caso, no importa si TargetType es un tipo de referencia o un tipo de valor.Puede haber otros casos que involucren genéricos donde
is
sea útil (porque es posible que no sepa si T es un tipo de referencia o no, por lo que no puede usar como), pero son relativamente oscuros.Es casi seguro que lo he usado antes
is
para el caso del tipo de valor, sin haber pensado en usar un tipo anulable yas
juntos :)EDITAR: tenga en cuenta que ninguno de los anteriores habla sobre el rendimiento, excepto el caso del tipo de valor, donde he notado que unboxing a un tipo de valor anulable es en realidad más lento, pero consistente.
De acuerdo con la respuesta de naasking, is-and-cast o is-and-as son tan rápidos como el chequeo nulo con los JIT modernos, como se muestra en el siguiente código:
En mi computadora portátil, todo esto se ejecuta en aproximadamente 60 ms. Dos cosas a tener en cuenta:
Así que no nos preocupemos por el rendimiento. Preocupémonos por la corrección y la consistencia.
Sostengo que is-and-cast (o is-and-as) no son seguros cuando se trata de variables, ya que el tipo del valor al que se refiere puede cambiar debido a otro hilo entre la prueba y el elenco. Esa sería una situación bastante rara, pero prefiero tener una convención que pueda usar de manera consistente.
También sostengo que el cheque nulo como entonces da una mejor separación de las preocupaciones. Tenemos una declaración que intenta una conversión, y luego una declaración que usa el resultado. Is-and-cast o is-and-as realiza una prueba y luego otro intento de convertir el valor.
Para decirlo de otra manera, ¿alguien escribiría alguna vez :
Eso es algo de lo que está haciendo el reparto, aunque obviamente de una manera bastante más barata.
fuente
if (randomObject is TargetType convertedRandomObject){ // Do stuff with convertedRandomObject.Value}
o usarswitch
/case
ver documentos"as" devolverá NULL si no es posible lanzarlo.
lanzar antes generará una excepción.
Para el rendimiento, generar una excepción suele ser más costoso en el tiempo.
fuente
Aquí hay otra respuesta, con alguna comparación IL. Considera la clase:
Ahora mire la IL que produce cada método. Incluso si los códigos operativos no significan nada para usted, puede ver una diferencia importante: isinst se llama seguido de castclass en el método DirectCast. Entonces, dos llamadas en lugar de una básicamente.
La palabra clave isinst versus la clase de cast
Esta publicación de blog tiene una comparación decente entre las dos formas de hacerlo. Su resumen es:
Personalmente siempre uso As, porque es fácil de leer y lo recomienda el equipo de desarrollo de .NET (o Jeffrey Richter de todos modos)
fuente
Una de las diferencias más sutiles entre los dos es que la palabra clave "as" no se puede usar para emitir cuando un operador de conversión está involucrado:
Esto no se compilará (aunque creo que lo hizo en versiones anteriores) en la última línea, ya que las palabras clave "como" no tienen en cuenta los operadores de conversión. Sin
string cast = (string)f;
embargo, la línea funciona bien.fuente
como nunca arroja una excepción si no puede realizar la conversión devolviendo nulo en su lugar ( ya que opera solo en tipos de referencia). Entonces, usar as es básicamente equivalente a
Los lanzamientos de estilo C, por otro lado, arrojan una excepción cuando no es posible la conversión.
fuente
No es realmente una respuesta a su pregunta, pero creo que es un punto relacionado importante.
Si está programando para una interfaz, no debería necesitar emitir. Esperemos que estos moldes sean muy raros. Si no, es probable que deba repensar algunas de sus interfaces.
fuente
Ignore el consejo de Jon Skeet, re: evite el patrón de prueba y reparto, es decir:
La idea de que esto cuesta más que un reparto y una prueba nula es un MITO :
Es una microoptimización que no funciona. Hice algunas pruebas reales , y test-and-cast es en realidad más rápido que la comparación de lanzamiento y nulo, y también es más seguro porque no tienes la posibilidad de tener una referencia nula en el ámbito fuera del if debería el lanzamiento fallar.
Si desea una razón por la cual la prueba y el lanzamiento es más rápida, o al menos no más lenta, hay una razón simple y compleja.
Simple: incluso los compiladores ingenuos fusionarán dos operaciones similares, como test-and-cast, en una sola prueba y rama. cast-and-null-test puede forzar dos pruebas y una rama, una para la prueba de tipo y conversión a nulo en caso de falla, una para la verificación nula en sí. Como mínimo, ambos se optimizarán para una sola prueba y ramificación, por lo que test-and-cast no sería ni más lento ni más rápido que cast-and-null-test.
Complejo: por qué test-and-cast es más rápido: cast-and-null-test introduce otra variable en el ámbito externo que el compilador debe rastrear para determinar la vida, y es posible que no pueda optimizar esa variable dependiendo de cuán complejo sea su control. el flujo es Por el contrario, test-and-cast introduce una nueva variable solo en un ámbito delimitado para que el compilador sepa que la variable está muerta después de que el ámbito finalice, y así puede optimizar mejor la asignación de registros.
Así que por favor, POR FAVOR, deje que este consejo de "probar y anular prueba sea mejor que probar y emitir" muera. POR FAVOR. test-and-cast es más seguro y más rápido.
fuente
ref
parámetro. Es seguro para las variables locales, pero no para los campos. Me interesaría ejecutar sus puntos de referencia, pero el código que ha proporcionado en su publicación de blog no está completo. Estoy de acuerdo con no micro-optimizar, pero no creo que usar el valor dos veces sea más legible o elegante que usar "como" y una prueba de nulidad. (Definitivamente usaría un elenco directo en lugar de "como" después de un es, por cierto.)Si el reparto falla, la palabra clave 'as' no arroja una excepción; establece la variable en nulo (o en su valor predeterminado para los tipos de valor) en su lugar.
fuente
Esta no es una respuesta a la pregunta, sino un comentario al ejemplo de código de la pregunta:
Por lo general, no debería tener que lanzar un objeto desde, por ejemplo, IMyInterface a MyClass. Lo mejor de las interfaces es que si toma un objeto como entrada que implementa una interfaz, no tiene que preocuparse de qué tipo de objeto está obteniendo.
Si lanza IMyInterface a MyClass, entonces ya asume que obtiene un objeto de tipo MyClass y no tiene sentido usar IMyInterface, porque si alimenta su código con otras clases que implementan IMyInterface, rompería su código ...
Ahora, mi consejo: si sus interfaces están bien diseñadas, puede evitar muchos tipos de letra.
fuente
El
as
operador solo se puede usar en tipos de referencia, no se puede sobrecargar y volveránull
si la operación falla. Nunca arrojará una excepción.La transmisión se puede usar en cualquier tipo compatible, se puede sobrecargar y generará una excepción si la operación falla.
La elección de cuál usar depende de las circunstancias. Principalmente, se trata de si desea lanzar una excepción en una conversión fallida.
fuente
Mi respuesta es solo sobre la velocidad en los casos en que no verificamos el tipo y no verificamos los valores nulos después del lanzamiento. Agregué dos pruebas adicionales al código de Jon Skeet:
Resultado:
No intentes concentrarte en la velocidad (como lo hice) porque todo esto es muy, muy rápido.
fuente
as
conversión (sin la comprobación de errores) se ejecutó aproximadamente un 1-3% más rápido que la conversión (alrededor de 540 ms frente a 550 ms en 100 millones de iteraciones). Ninguno de los dos hará o interrumpirá su solicitud.Además de todo lo que ya se expuso aquí, acabo de encontrar una diferencia práctica que creo que vale la pena señalar, entre el casting explícito
versus usar el
as
operador.Aquí está el ejemplo:
En pocas palabras : GenericCaster2 no funcionará con tipos de estructura. GenericCaster lo hará.
fuente
Si usa los PIA de Office dirigidos a .NET Framework 4.X, debe usar la palabra clave como , de lo contrario, no se compilará.
Sin embargo, la transmisión está bien cuando se dirige a .NET 2.0:
Al apuntar a .NET 4.X los errores son:
error CS0656: Falta compilador miembro requerido 'Microsoft.CSharp.RuntimeBinder.Binder.Convert'
error CS0656: falta compilador miembro requerido 'Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create'
fuente
La
as
palabra clave funciona igual que una conversión explícita entre tipos de referencia compatibles con la gran diferencia de que no genera una excepción si la conversión falla. Más bien, produce un valor nulo en la variable objetivo. Dado que las Excepciones son muy caras en términos de rendimiento, se considera un método de lanzamiento mucho mejor.fuente
Lo que elija depende en gran medida de lo que requiera. Prefiero casting explícito
porque si el objeto debería ser del tipo IMyInterface y no lo es, definitivamente es un problema. Es mejor obtener el error lo antes posible porque se solucionará el error exacto en lugar de corregir su efecto secundario.
Pero si maneja métodos que aceptan
object
como parámetro, entonces debe verificar su tipo exacto antes de ejecutar cualquier código. En tal casoas
sería útil para que puedas evitarloInvalidCastException
.fuente
Depende, ¿desea verificar si es nulo después de usar "como" o prefiere que su aplicación arroje una excepción?
Mi regla general es que si siempre espero que la variable sea del tipo que estoy esperando en el momento en que quiero, uso un yeso. Si es posible que la variable no se convierta en lo que quiero y estoy preparado para manejar los valores nulos de usar as, usaré as.
fuente
Echa un vistazo a estos enlaces:
Te muestran algunos detalles y pruebas de rendimiento.
fuente
El problema del OP se limita a una situación de lanzamiento específica. El título cubre muchas más situaciones.
Aquí hay una descripción general de todas las situaciones de casting relevantes en las que actualmente puedo pensar:
fuente