2010-08-13 19 views
6

Duplicar posible:
Casting: (NewType) vs. Object as NewType¿Por qué alguna vez invocar tipos de referencia cuando puede usar "como"?

En C#, qué tipos de referencia cada vez emitidos cuando se puede usar "como"?

La conversión puede generar excepciones mientras que "as" se evacúa a null si falla la conversión.

¿No sería "fácil" usar "como" con los tipos de referencia en todos los casos?

por ejemplo:

MyObject as DataGridView 

en lugar de,

(DataGridView)MyObject 
+0

@Jarrett Meyer - esa pregunta pregunta cuál es la diferencia. Esta pregunta es diferente Al saber la diferencia, pregunta por qué un lanzamiento que lanza es mejor que un lanzamiento que no lanza. –

Respuesta

2

operador 'como' trabajo con los tipos de referencia solamente.

+3

También 'falla' silenciosamente. – Noldorin

+0

La pregunta se ha actualizado para aclarar que se trata solo de tipos de referencia (ya que se trata de 'null', pensé que ya estaba bastante claro). –

2

A veces, desea que se emita la excepción. A veces, quieres intentar convertir y los nulos están bien. Como ya se indicó, as no funcionará con tipos de valores.

+0

Maldita sea, me gusta;) exactamente, a veces quieres que se haga una excepción, lo cual es perfectamente válido. +1 – Kezzer

+0

¿Por qué querría que se lanzara la excepción? – CJ7

+0

Porque el código de llamada ha hecho algo que no tiene sentido y la única respuesta sensata es arrojar una excepción. No es diferente a la forma en que si llamó a Load() en un XMLDocument con contenido que no era XML, entonces desea que arroje una excepción. No hacerlo te deja con un objeto que no se puede usar con sensatez, pero que no sabes que no se puede usar con sensatez. –

0

De MSDN (as (C# reference)):

el operador as sólo realiza las conversiones de referencia y las conversiones de boxeo. El operador as no puede realizar otras conversiones, como las conversiones definidas por el usuario, que en su lugar deberían realizarse utilizando expresiones de conversión.

0

Teniendo en cuenta todos los comentarios, nos topamos con esto el otro día y nos preguntamos por qué harías un lanzamiento directo sobre el uso de la palabra clave as. ¿Qué pasa si quiere que el molde falle? Este es a veces el efecto deseado que desea de un yeso si está lanzando desde un objeto nulo. A continuación, inserta la excepción en la pila de llamadas.

Por lo tanto, si desea que algo falle, use un molde directo, si está de acuerdo con que no falle, use la palabra clave as.

+0

¿Por qué querrías que fallara el lanzamiento?Además, si el lanzamiento falla usando "como", se te dará nulo. – CJ7

+0

Algunos entornos de software atienden a tipos genéricos, como el nuestro, para aumentar la flexibilidad. Se pueden introducir nuevos tipos ad-hoc. Por ejemplo, utilizamos ruedas dinámicas que pueden convertir de un tipo a otro aunque no estén relacionadas. Esto se hace para que el tipo de final sea "fácil de usar" (quizás inmutable, o con campos ocultos). Si el yeso no funciona, ¡tenemos que saberlo! – Kezzer

1

Si, cuando escribe el código para hacer el molde, está seguro de que el molde debería funcionar, debe usar (DataGridView)MyObject. De esta forma, si el molde falla en el futuro, su suposición sobre el tipo de MyObject causará una excepción de lanzamiento no válido en el punto donde realiza el lanzamiento, en lugar de una excepción de referencia nula en algún momento posterior.

Si desea manejar el caso donde MyObject no es un DataGridView, entonces use as, y presumiblemente verifique que sea nulo antes de hacer algo con él.

tl; dr Si su código asume algo, y esa suposición es incorrecta en tiempo de ejecución, el código debería arrojar una excepción.

9

considerar las siguientes alternativas:

Foo(someObj as SomeClass); 

y:

Foo((SomeClass)someObj); 

Debido a ser someObj del tipo incorrecto, la primera versión cruza por null a Foo. Algún tiempo después, esto da como resultado un lanzamiento de NullReferenceException. ¿Cuánto más tarde? Depende de lo que haga Foo. Puede almacenar el null en un campo, y minutos más tarde se accede por algún código que espera que no sea null.

Pero con la segunda versión, encuentra el problema inmediatamente.

¿Por qué hacer que sea más difícil arreglar los errores?

actualización

El PO se le preguntó en un comentario: ¿No es más fácil de usar as y para verificar si null en un comunicado por if?

Si el null es inesperado y es evidencia de un error en la persona que llama, se podría decir:

SomeClass c = someObj as SomeClass; 
if (c == null) 
{ 
    // hmm... 
} 

¿Qué hacer en ese if -bloque? Hay dos soluciones generales. Uno es lanzar una excepción, por lo que es responsabilidad del que llama para tratar su error. En cuyo caso es definitivamente más simple de escribir:

SomeClass c = (SomeClass)someObj; 

Simplemente le ahorra escribir la lógica if/throw a mano.

Sin embargo, existe otra alternativa. Si tiene una implementación "estándar" de SomeClass que está contento de usar donde no hay nada mejor disponible (tal vez tiene métodos que no hacen nada, o devuelven valores "vacíos", etc.) entonces puede hacer esto:

SomeClass c = (someObj as SomeClass) ?? _stockImpl; 

Esto asegurará que c nunca sea null. ¿Pero es eso realmente mejor? ¿Qué pasa si la persona que llama tiene un error? ¿No quieres ayudar a encontrar errores? Al intercambiar en un objeto predeterminado, disimula el error. Eso suena como una idea atractiva hasta que pierdes una semana de tu vida tratando de localizar un error.

(En cierto modo esto imita el comportamiento de Objective-C, en el que cualquier intento de utilizar una referencia null nunca va a tirar;. Simplemente en silencio no hace nada)

+0

+1 Sigo viendo 'Foo (someObj como SomeClass);' en el código (sin una verificación 'null', eso es). Me desconcierta por qué la gente hace esto. ¿Hay algún libro que diga '(SomeClass)' es malo? –

+0

¿No es más fácil comprobar si la variable es nula después de la declaración "como", en lugar de tratar con una excepción? – CJ7

+0

¿Se puede hacer algo útil cuando 'someObj' es' nulo'? Si no es así, si su código puede suponer que 'someObj' es en realidad' SomeClass', ¿cuál es el punto? –

1

Una razón clara es que el objeto es, o podría ser (al escribir un método genérico, puede que no se sepa en el momento de la codificación) que se transfiera a un tipo de valor, en cuyo caso as no está permitido.

Una razón más dudosa es que usted ya sabe que el objeto es del tipo en cuestión. Cuán dudoso depende de cómo usted ya lo sepa.En el siguiente caso:

if(obj is MyType) 
    DoMyTypeStuff((MyType)obj); 
else 
    DoMoreGeneralStuff(obj); 

Es difícil justificar el uso de as aquí, ya que lo único que hace es añadir una comprobación redundante (tal vez va a ser optimizado de distancia, tal vez no lo hará). En el otro extremo, si estás a medio camino de un estado de trance con la cantidad de información que tienes, tu cerebro está paginado en la memoria y sobre la base de eso estás bastante seguro de que el objeto debe ser del escriba en cuestión, quizás sea mejor agregar el cheque.

Otra buena razón es que la diferencia entre ser del tipo incorrecto y ser nulo queda oculto por as. Si es razonable pasar una cadena a un método dado, incluyendo una cadena nula, pero no es razonable pasar una int, entonces val as string acaba de hacer que el uso incorrecto parezca un uso correcto completamente diferente, y usted acaba de hizo que el error fuera más difícil de encontrar y potencialmente más dañino.

Finalmente, tal vez si no conoce el tipo de objeto, el código de llamada debería. Si el código de llamada ha llamado al suyo incorrectamente, deberían recibir una excepción. Permitir que InvalidCastException se devuelva, o atraparlo y lanzar una excepción InvalidArgument o similar es un medio razonable y claro de hacerlo.

+0

'MyType t = obj como MyType; if (t! = null) {DoMyTypeStuff (t); } else {DoMoreGeneralStuff (obj); } ' –

0

Como es más rápido y no arroja excepciones. Por lo tanto, es generalmente preferido. Las razones para usar moldes incluyen:

Al usar como, solo puede asignar tipos que son inferiores en el árbol de herencia a los que son más altos. Por ejemplo:

object o = "abc" as object; 
DataGridView d = "abc" as DataGridView // doesn't do anything 

DataGridView podría crear una conversión personalizada que sí lo permita. Los lanzamientos se definen en el tipo de objetivo y, por lo tanto, permiten todo, siempre que esté definido.

Otro problema es que no siempre funciona. Considere este método:

IEnumerable<T> GetList<T>(T item) 
{ 
    (from ... select) as IEnumerable<T> 
} 

Este código falla porque T también podría ser un tipo de valor. No puede usarlos como tales porque nunca pueden ser nulos. Esto significa que tendrás que poner una restricción en T, mientras que en realidad es innecesario. Si no sabe si va a tener un tipo de referencia o no, nunca podrá usar como.

Por supuesto, siempre debe verificar la nula cuando utiliza la palabra clave como. No asuma que no se lanzarán excepciones solo porque la palabra clave no arroja ninguna. No ponga un Try {} Catch(NullReferenceException){} alrededor de eso, eso no es necesario y se hincha. Simplemente asigne el valor a una variable y busque null antes de usarlo. Nunca lo use en línea en una llamada a método.

Cuestiones relacionadas