Em blogs de desenvolvimento, exemplos de código on-line e (recentemente) até um livro, continuo tropeçando em códigos como este:
var y = x as T;
y.SomeMethod();
ou ainda pior:
(x as T).SomeMethod();
Isso não faz sentido para mim. Se você tem certeza que x
é do tipo T
, você deve usar um elenco direta: (T)x
. Se você não tiver certeza, pode usá-lo, as
mas precisa verificar null
antes de executar alguma operação. Tudo o que o código acima faz é transformar um (útil) InvalidCastException
em (inútil) NullReferenceException
.
Eu sou o único que acha que isso é um abuso flagrante da as
palavra - chave? Ou eu perdi algo óbvio e o padrão acima realmente faz sentido?
c#
casting
type-conversion
Heinzi
fonte
fonte
((T)x).SomeMethod()
, não é? ;) (brincadeirinha, você está certo, é claro!)(f as T).SomeMethod()
Respostas:
Sua compreensão é verdadeira. Isso parece tentar otimizar para mim. Você deve usar um elenco normal quando tiver certeza do tipo. Além de gerar uma exceção mais sensata, também falha rapidamente. Se você está errado sobre a sua suposição sobre o tipo, o programa irá falhar imediatamente e você será capaz de ver a causa da falha imediatamente em vez de esperar por um
NullReferenceException
ouArgumentNullException
ou mesmo um algum erro lógico no futuro. Em geral, umaas
expressão que não é seguida por umanull
verificação em algum lugar é um cheiro de código.Por outro lado, se você não tem certeza sobre o elenco e espera que ele falhe, use-o em
as
vez de um elenco normal envolvido com umtry-catch
bloco. Além disso,as
recomenda-se o uso de uma verificação de tipo seguida por uma conversão. Ao invés de:que gera uma
isinst
instrução para ais
palavra - chave e umacastclass
instrução para o elenco (realizando o elenco duas vezes), você deve usar:Isso gera apenas uma
isinst
instrução. O método anterior possui uma falha em potencial em aplicativos multithread, pois uma condição de corrida pode fazer com que a variável mude de tipo após ais
verificação ser bem-sucedida e falhar na linha de conversão. O último método não é propenso a esse erro.A solução a seguir não é recomendada para uso em código de produção. Se você realmente odeia uma construção fundamental em C #, considere mudar para o VB ou algum outro idioma.
Caso alguém odeie desesperadamente a sintaxe do elenco, ele / ela pode escrever um método de extensão para imitar o elenco:
e use uma sintaxe pura [?]:
fonte
cache
objeto que outro thread tente invalidá-lo, definindo-o comonull
. Em cenários sem bloqueio, esse tipo de coisa pode surgir.Object
. O uso do método em um tipo de valor fará com que ele seja encaixotado desnecessariamente.To
método aqui, pois apenas converte na hierarquia de herança, o que para tipos de valor envolve boxe de qualquer maneira. Obviamente, toda a ideia é mais teórica do que séria.IMHO,
as
apenas faz sentido quando combinado com umanull
verificação:fonte
O uso de 'as' não aplica conversões definidas pelo usuário, enquanto o elenco as utilizará quando apropriado. Isso pode ser uma diferença importante em alguns casos.
fonte
Eu escrevi um pouco sobre isso aqui:
http://blogs.msdn.com/ericlippert/archive/2009/10/08/what-s-the-difference-between-as-and-cast-operators.aspx
Eu entendo o seu ponto. E eu concordo com o objetivo: que um operador de conversão se comunique "Tenho certeza de que esse objeto pode ser convertido para esse tipo e estou disposto a arriscar uma exceção se estiver errado", enquanto um operador "como" se comunica "Não tenho certeza de que este objeto possa ser convertido para esse tipo; me dê um nulo se estiver errado".
No entanto, há uma diferença sutil. (x como T). O que quer que () comunique "Eu sei não apenas que x pode ser convertido em um T, mas, além disso, que isso envolve apenas conversões de referência ou unboxing e, além disso, que x não é nulo". Isso comunica informações diferentes de ((T) x). Qualquer que seja (), e talvez seja isso que o autor do código pretende.
fonte
((T)x).Whatever()
também comunica quex
não se destina a ser nulo, e eu duvido muito que um autor normalmente se importe se a conversãoT
ocorre apenas com conversões de referência ou de unboxing, ou se requer uma conversão definida pelo usuário ou que altere a representação. Afinal, se eu definirpublic static explicit operator Foo(Bar b){}
, é claramente minha intenção queBar
seja considerada compatívelFoo
. É raro que eu queira evitar essa conversão.Eu sempre vi referências a este artigo enganoso como evidência de que "como" é mais rápido que o lançamento.
Um dos aspectos enganosos mais óbvios deste artigo é o gráfico, que não indica o que está sendo medido: suspeito que esteja medindo as projeções com falha (onde "como" é obviamente muito mais rápido, pois nenhuma exceção é lançada).
Se você dedicar algum tempo para fazer as medições, verá que o elenco é, como seria de esperar, mais rápido que "as" quando o elenco é bem-sucedido.
Suspeito que esse seja um dos motivos para o uso de "cult de carga" da palavra-chave as em vez de um elenco.
fonte
A conversão direta precisa de um par de parênteses mais do que a
as
palavra - chave. Portanto, mesmo no caso de você ter 100% de certeza do tipo, isso reduz a confusão visual.No entanto, concordamos com a exceção. Mas pelo menos para mim, a maioria dos usos se
as
resume a procurarnull
depois, o que acho melhor do que pegar uma exceção.fonte
99% do tempo em que uso "como" ocorre quando não tenho certeza de qual é o tipo de objeto real
e eu não quero capturar exceções explícitas de elenco nem fazer elenco duas vezes, usando "is":
fonte
as
. Qual é o outro 1%?É só porque as pessoas gostam da aparência, é muito legível.
Vamos ser sinceros: o operador de conversão / conversão em idiomas do tipo C é bastante terrível, em termos de legibilidade. Eu gostaria que o C # adotasse a sintaxe Javascript de:
Ou defina um
to
operador, o equivalente de conversão deas
:fonte
dynamic_cast<>()
(e similar). Você está fazendo algo feio, deve parecer feio.As pessoas gostam
as
tanto porque as faz se sentirem protegidas de exceções ... Como garantia em uma caixa. Um cara coloca uma garantia chique na caixa, porque ele quer que você se sinta todo quente e quentinho por dentro. Você acha que coloca essa caixinha debaixo do travesseiro à noite, a Fada da Garantia pode descer e deixar um quarto, não é mesmo, Ted?Voltar ao tópico ... ao usar uma conversão direta, existe a possibilidade de uma exceção de conversão inválida. Portanto, as pessoas se aplicam
as
como uma solução geral para todas as suas necessidades de fundição porqueas
(por si só) nunca lançará uma exceção. Mas o engraçado é que, no exemplo que você deu(x as T).SomeMethod();
você está trocando uma exceção de conversão inválida por uma exceção de referência nula. O que ofusca o problema real quando você vê a exceção.Eu geralmente não uso
as
muito. Eu prefiro ois
teste porque, para mim, parece mais legível e faz mais sentido do que tentar um elenco e verificar se há nulo.fonte
as
como uma solução geral porque as faz se sentirem seguras.Essa deve ser uma das minhas maiores irritações .
O D&E da Stroustrup e / ou algum post do blog que não consigo encontrar agora discute a noção de
to
operador que abordaria o argumento de https://stackoverflow.com/users/73070/johannes-rossel (ou seja, a mesma sintaxe queas
mas comDirectCast
semântica) )A razão pela qual isso não foi implementado é porque um elenco deve causar dor e ser feio, para que você se afaste do uso.
Pena que programadores 'inteligentes' (muitas vezes autores de livros (Juval Lowy IIRC)) contornem isso abusando
as
dessa maneira (o C ++ não ofereceas
, provavelmente por esse motivo).Mesmo o VB tem mais consistência em ter uma sintaxe uniforme que o força a escolher um
TryCast
ouDirectCast
e decidir-se !fonte
DirectCast
comportamento , não sintaxe .semantics
: Pdouble-to-int
elenco que falharia sedouble
não representasse um valor exato que pudesse caber em umInt32
, mas ter(int)-1.5
rendimento -1 é simplesmente feio.MaybeValid<T>
com dois campos públicosIsValid
eValue
qual código poderia fazer como achar melhor. Isso teria permitido, por exemploMaybeValid<TValue> TryGetValue(TKey key) { var ret = default(MaybeValid<TValue>); ret.IsValid = dict.TryGetValue(key, out ret.Value); return ret; }
. Isso não apenas salvaria pelo menos duas operações de cópia em comparação comNullable<T>
, mas também poderia valer com qualquer tipoT
- e não apenas classes.Acredito que a
as
palavra - chave possa ser vista como uma versão mais elegantedynamic_cast
do C ++.fonte
dynamic_cast
com C ++.std::bad_cast
.static_cast
não executa nenhuma verificação do tipo de tempo de execução. Não há elenco semelhante a isso em C #.Provavelmente é mais popular sem motivo técnico, mas apenas porque é mais fácil de ler e mais intuitivo. (Sem dizer que é melhor apenas tentar responder à pergunta)
fonte
Um motivo para usar "como":
Em vez de (código incorreto):
fonte
obj
significaria alterar aobj
própria variável para manter uma referência a outro objeto. Não alteraria o conteúdo da memória na qual reside o objeto originalmente referenciado porobj
. Esse objeto original permaneceria inalterado e at
variável ainda manteria uma referência a ele.