En blogs de desarrollo, ejemplos de código en línea e (recientemente) incluso un libro, sigo tropezando con código como este:
var y = x as T;
y.SomeMethod();
o, peor aún:
(x as T).SomeMethod();
Eso no tiene sentido para mí. Si está seguro de que x
es de tipo T
, se debe utilizar un molde directo: (T)x
. Si no está seguro, puede usarlo, as
pero debe verificarnull
antes de realizar alguna operación. Todo lo que hace el código anterior es convertir un (útil) InvalidCastException
en un (inútil) NullReferenceException
.
¿Soy el único que piensa que esto es un abuso flagrante de la as
palabra clave? ¿O me perdí algo obvio y el patrón anterior realmente tiene sentido?
c#
casting
type-conversion
Heinzi
fuente
fuente
((T)x).SomeMethod()
, ¿no? ;) (es broma, ¡tienes razón, por supuesto!)(f as T).SomeMethod()
Respuestas:
Tu comprensión es verdadera. Eso suena como tratar de micro-optimizar para mí. Debe usar un yeso normal cuando esté seguro del tipo. Además de generar una excepción más sensata, también falla rápidamente. Si se equivoca sobre su hipótesis sobre el tipo, el programa fallará inmediatamente y usted será capaz de ver la causa del fracaso de inmediato en lugar de esperar a que una
NullReferenceException
oArgumentNullException
ni siquiera un error lógico en algún momento en el futuro. En general, unaas
expresión que no es seguida por unnull
cheque en alguna parte es un olor a código.Por otro lado, si no está seguro sobre el yeso y espera que falle, debe usarlo en
as
lugar de un yeso normal envuelto con untry-catch
bloque. Además,as
se recomienda el uso de una verificación de tipo seguida de un yeso. En vez de:que genera una
isinst
instrucción para lais
palabra clave y unacastclass
instrucción para el reparto (realizar efectivamente el reparto dos veces), debe usar:Esto solo genera una
isinst
instrucción. El primer método tiene una falla potencial en aplicaciones de subprocesos múltiples, ya que una condición de carrera puede hacer que la variable cambie su tipo después de que lais
verificación se haya realizado correctamente y falle en la línea de lanzamiento. El último método no es propenso a este error.La siguiente solución no se recomienda para su uso en el código de producción. Si realmente odias una construcción tan fundamental en C #, podrías considerar cambiar a VB u otro lenguaje.
En caso de que uno odie desesperadamente la sintaxis del elenco, puede escribir un método de extensión para imitar el elenco:
y use una sintaxis ordenada [?]:
fuente
cache
objeto que otro hilo intenta invalidar configurándolo ennull
. En escenarios sin bloqueo, pueden surgir este tipo de cosas.Object
. El uso del método en un tipo de valor hará que se encuadre innecesariamente.To
método aquí, ya que solo se convierte en la jerarquía de herencia, lo que para los tipos de valor implica el boxeo de todos modos. Por supuesto, toda la idea es más teórica que seria.En mi humilde opinión,
as
tiene sentido cuando se combina con unnull
cheque:fuente
El uso de 'as' no aplica las conversiones definidas por el usuario, mientras que el elenco las usará cuando corresponda. Esa puede ser una diferencia importante en algunos casos.
fuente
Escribí un poco sobre esto aquí:
http://blogs.msdn.com/ericlippert/archive/2009/10/08/what-s-the-difference-between-as-and-cast-operators.aspx
Entiendo tu punto. Y estoy de acuerdo con el objetivo: que un operador de reparto comunica "Estoy seguro de que este objeto se puede convertir a ese tipo, y estoy dispuesto a arriesgar una excepción si me equivoco", mientras que un operador "como" se comunica "No estoy seguro de que este objeto se pueda convertir a ese tipo; dame un valor nulo si me equivoco".
Sin embargo, hay una sutil diferencia. (x como T). Lo que sea que () comunique "Sé no solo que x puede convertirse en una T, sino que, además, eso implica solo conversiones de referencia o unboxing y, además, que x no es nulo". Eso comunica información diferente a ((T) x). Lo que sea (), y quizás eso es lo que pretende el autor del código.
fuente
((T)x).Whatever()
también comunica quex
no está [destinado a ser] nulo, y dudo mucho que un autor normalmente se preocupe si la conversión seT
produce solo con conversiones de referencia o unboxing, o si requiere una conversión definida por el usuario o que cambie la representación. Después de todo, si lo definopublic static explicit operator Foo(Bar b){}
, entonces es claramente mi intención la queBar
se considera compatible conFoo
. Es raro que quiera evitar esta conversión.A menudo he visto referencias a este artículo engañoso. como evidencia de que "as" es más rápido que el casting.
Uno de los aspectos engañosos más obvios de este artículo es el gráfico, que no indica lo que se está midiendo: sospecho que la medición ha fallado lanzamientos (donde "as" es obviamente mucho más rápido ya que no se produce ninguna excepción).
Si se toma el tiempo para hacer las mediciones, verá que el lanzamiento es, como era de esperar, más rápido que "como" cuando el lanzamiento tiene éxito.
Sospecho que esta puede ser una razón para el uso de "culto de carga" de la palabra clave como en lugar de un elenco.
fuente
El reparto directo necesita un par de paréntesis más que la
as
palabra clave. Entonces, incluso en el caso de que esté 100% seguro de cuál es el tipo, reduce el desorden visual.Sin embargo, estuvo de acuerdo en lo de la excepción. Pero al menos para mí, la mayoría de los usos de
as
reducir se reducen para verificarnull
después, lo que me parece más agradable que detectar una excepción.fuente
El 99% de las veces que uso "como" es cuando no estoy seguro de cuál es el tipo de objeto real
y no quiero capturar excepciones explícitas ni emitir dos veces, usando "is":
fuente
as
. ¿Cuál es el otro 1%?Es solo porque a la gente le gusta cómo se ve, es muy legible.
Seamos realistas: el operador de conversión / conversión en lenguajes tipo C es bastante terrible, en cuanto a legibilidad. Me gustaría que C # adoptara la sintaxis Javascript de:
O defina un
to
operador, el equivalente de lanzamiento deas
:fuente
dynamic_cast<>()
(y similar). Estás haciendo algo feo, debería verse feo.A la gente le gusta
as
mucho porque los hace sentir a salvo de las excepciones ... Como garantía en una caja. Un chico pone una garantía elegante en la caja porque quiere que te sientas cálido y tostado por dentro. Crees que pones esa cajita debajo de tu almohada por la noche, el Hada de la Garantía podría bajar y dejar un cuarto, ¿estoy en lo cierto, Ted?Volviendo al tema ... cuando se usa una transmisión directa, existe la posibilidad de una excepción de transmisión no válida. Por lo tanto, las personas se aplican
as
como una solución general a todas sus necesidades de lanzamiento porqueas
(por sí solo) nunca arrojará una excepción. Pero lo curioso de eso, está en el ejemplo que diste(x as T).SomeMethod();
, está intercambiando una excepción de conversión no válida por una excepción de referencia nula. Lo que ofusca el verdadero problema cuando ves la excepción.Generalmente no uso
as
demasiado. Prefiero lais
prueba porque, para mí, parece más legible y tiene más sentido que probar un yeso y verificar si es nulo.fuente
as
como una solución general porque les hace sentirse seguras.Este tiene que ser uno de mis principales manías .
D&E de Stroustrup y / o alguna publicación de blog que no puedo encontrar en este momento discute la noción de un
to
operador que abordaría el punto hecho por https://stackoverflow.com/users/73070/johannes-rossel (es decir, la misma sintaxisas
pero conDirectCast
semántica )La razón por la que esto no se implementó es porque un yeso debería causar dolor y ser feo, por lo que no podrá usarlo.
Lástima que los programadores 'inteligentes' (a menudo autores de libros (Juval Lowy IIRC)) lo eviten abusando
as
de esta manera (C ++ no ofrece unaas
, probablemente por esta razón).¡Incluso VB tiene más consistencia al tener una sintaxis uniforme que te obliga a elegir un
TryCast
oDirectCast
y tomar una decisión !fuente
DirectCast
comportamiento , no sintaxis .semantics
en su lugar: Pdouble-to-int
lanzamiento que fallaría sidouble
no representara un valor exacto que pudiera caber en unInt32
, pero tener un(int)-1.5
rendimiento -1 es simplemente feo.MaybeValid<T>
con dos campos públicosIsValid
yValue
qué código podría resolver como mejor le parezca. Eso hubiera permitido, por ejemploMaybeValid<TValue> TryGetValue(TKey key) { var ret = default(MaybeValid<TValue>); ret.IsValid = dict.TryGetValue(key, out ret.Value); return ret; }
. Eso no solo ahorraría al menos dos operaciones de copia en comparaciónNullable<T>
, sino que también podría valer con cualquier tipo, noT
solo clases.Creo que la
as
palabra clave podría considerarse como una versión más elegantedynamic_cast
de C ++.fuente
dynamic_cast
a C ++.std::bad_cast
.static_cast
no realiza ninguna verificación del tipo de tiempo de ejecución. No hay un reparto similar a esto en C #.Probablemente sea más popular sin ninguna razón técnica, sino solo porque es más fácil de leer y más intuitivo. (No decir que lo hace mejor solo tratando de responder la pregunta)
fuente
Una razón para usar "como":
En lugar de (código incorrecto):
fuente
obj
significaría cambiar laobj
variable en sí para contener una referencia a otro objeto. No alteraría el contenido de la memoria en la que reside el objeto originalmente referenciado porobj
. Este objeto original permanecería sin cambios, y lat
variable aún tendría una referencia a él.