Ao programar interfaces, descobri que estou fazendo muitas conversões de conversão ou de tipo de objeto.
Existe uma diferença entre esses dois métodos de conversão? Em caso afirmativo, existe uma diferença de custo ou como isso afeta meu 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);
}
}
Além disso, o que é "em geral" o método preferido?
Respostas:
A resposta abaixo da linha foi escrita em 2008.
O C # 7 introduziu a correspondência de padrões, que substituiu amplamente o
as
operador, como agora você pode escrever:Observe que
tt
ainda está no escopo depois disso, mas não está definitivamente atribuído. ( Definitivamente, é atribuído dentro doif
corpo.) Isso é um pouco irritante em alguns casos, por isso, se você realmente se importa em introduzir o menor número possível de variáveis em todos os escopos, você ainda pode querer usaris
seguido de uma conversão.Eu não acho que nenhuma das respostas até agora (no momento de iniciar essa resposta!) Tenha realmente explicado onde vale a pena usar quais.
Não faça isso:
Essa verificação não é apenas duas vezes, mas também pode estar verificando coisas diferentes, se
randomObject
for um campo e não uma variável local. É possível que o "se" passe, mas o elenco falhe, se outro encadeamento alterar o valorrandomObject
entre os dois.Se
randomObject
realmente deve ser uma instância deTargetType
, ou seja, se não for, isso significa que há um erro, então a conversão é a solução certa. Isso gera uma exceção imediatamente, o que significa que não há mais trabalho sob suposições incorretas e a exceção mostra corretamente o tipo de bug.Se
randomObject
pode ser uma instânciaTargetType
eTargetType
é um tipo de referência, use um código como este:Se
randomObject
pode ser uma instânciaTargetType
eTargetType
é um tipo de valor, não podemos usaras
comTargetType
ele mesmo, mas podemos usar um tipo que permite valor nulo:(Nota: atualmente, na verdade, isso é mais lento que o + elenco . Acho que é mais elegante e consistente, mas lá vamos nós.)
Se você realmente não precisa do valor convertido, mas precisa saber se é uma instância do TargetType, o
is
operador é seu amigo. Nesse caso, não importa se TargetType é um tipo de referência ou um valor.Pode haver outros casos envolvendo genéricos onde
is
é útil (porque você pode não saber se T é um tipo de referência ou não, então não pode usá-lo como), mas eles são relativamente obscuros.Eu quase certamente usei
is
para o caso do tipo de valor antes, agora, sem ter pensado em usar um tipo anulável eas
juntos :)EDIT: Observe que nenhuma das opções acima fala sobre desempenho, exceto o caso do tipo de valor, em que observei que a descompactação para um tipo de valor nulo é realmente mais lenta - mas consistente.
De acordo com a resposta de naasking, o is-and-cast ou is-as-são tão rápidos quanto a verificação de nulos e com JITs modernos, como mostra o código abaixo:
No meu laptop, tudo isso é executado em cerca de 60ms. Duas coisas a serem observadas:
Então não vamos nos preocupar com o desempenho. Vamos nos preocupar com correção e consistência.
Eu mantenho que o is-and-cast (ou é-e-as) é inseguro ao lidar com variáveis, pois o tipo de valor a que ele se refere pode mudar devido a outro encadeamento entre o teste e o cast. Essa seria uma situação bastante rara - mas prefiro ter uma convenção que possa usar de forma consistente.
Também mantenho que a verificação como então nula oferece uma melhor separação de preocupações. Temos uma declaração que tenta uma conversão e, em seguida, uma declaração que usa o resultado. O is-and-cast ou is-and-as executa um teste e, em seguida, outra tentativa de converter o valor.
Para colocar de outra forma, alguém sempre escreve:
Isso é o que o elenco está fazendo - embora obviamente de uma maneira um pouco mais barata.
fonte
if (randomObject is TargetType convertedRandomObject){ // Do stuff with convertedRandomObject.Value}
ou usoswitch
/case
ver docs"as" retornará NULL se não for possível converter.
a transmissão antes criará uma exceção.
Para o desempenho, gerar uma exceção geralmente é mais caro no tempo.
fonte
Aqui está outra resposta, com algumas comparações de IL. Considere a classe:
Agora observe a IL que cada método produz. Mesmo se os códigos operacionais não significam nada para você, você pode ver uma grande diferença - isinst está sendo chamado seguido por castclass no método DirectCast. Então, duas chamadas em vez de uma basicamente.
A palavra-chave isinst versus a classe de elenco
Esta postagem no blog tem uma comparação decente entre as duas maneiras de fazer isso. Seu resumo é:
Pessoalmente, sempre uso o As, porque é fácil de ler e é recomendado pela equipe de desenvolvimento .NET (ou mesmo Jeffrey Richter)
fonte
Uma das diferenças mais sutis entre as duas é que a palavra-chave "as" não pode ser usada para conversão quando um operador de conversão está envolvido:
Isso não será compilado (embora eu ache que ocorreu nas versões anteriores) na última linha, pois as palavras-chave "as" não levam em consideração os operadores de conversão. A linha
string cast = (string)f;
funciona muito bem.fonte
como nunca lança uma exceção se não puder executar a conversão retornando nulo ( como opera apenas em tipos de referência). Então, usar como é basicamente equivalente a
As conversões no estilo C, por outro lado, lançam uma exceção quando nenhuma conversão é possível.
fonte
Não é realmente uma resposta para sua pergunta, mas o que eu acho é um ponto relacionado importante.
Se você estiver programando para uma interface, não precisará converter. Esperamos que esses lançamentos sejam muito raros. Caso contrário, você provavelmente precisará repensar algumas de suas interfaces.
fonte
Por favor, ignore o conselho de Jon Skeet, re: evite o teste e teste de elenco, ou seja:
A ideia de que isso custa mais do que um elenco e um teste nulo é um MITO :
É uma micro-otimização que não funciona. Fiz alguns testes reais e o teste-e-conversão é realmente mais rápido que a comparação de conversão-e-nulo, e também é mais seguro, porque você não tem a possibilidade de ter uma referência nula no escopo fora do if, caso o elenco falhou.
Se você deseja um motivo pelo qual o teste e a conversão são mais rápidos, ou pelo menos não mais lentos, existe um motivo simples e complexo.
Simples: até compiladores ingênuos unirão duas operações semelhantes, como test-and-cast, em um único teste e ramificação. teste de conversão e nulo pode forçar dois testes e uma ramificação, um para o teste de tipo e a conversão para nulo em caso de falha, um para a verificação nula. No mínimo, os dois otimizarão para um único teste e ramificação, para que o teste e a conversão não sejam mais lentos nem mais rápidos que o teste de conversão e nulo.
Complexo: por que o teste e a conversão são mais rápidos: o teste de conversão e nulo introduz outra variável no escopo externo, que o compilador deve rastrear quanto à disponibilidade, e talvez não seja possível otimizar essa variável, dependendo da complexidade do seu controle. fluxo é. Por outro lado, o teste e conversão introduz uma nova variável apenas em um escopo delimitado, para que o compilador saiba que a variável está inoperante após a saída do escopo e, assim, otimize melhor a alocação de registros.
Portanto, por favor, deixe que este conselho "teste de elenco e nulo seja melhor do que o teste e elenco" DIE. POR FAVOR. o teste e elenco é mais seguro e mais rápido.
fonte
ref
parâmetro. É seguro para variáveis locais, mas não para campos. Eu estaria interessado em executar seus benchmarks, mas o código que você forneceu em sua postagem no blog não está completo. Concordo em não otimizar a otimização, mas não acho que usar o valor duas vezes seja mais legível ou elegante do que usar "como" e um teste de nulidade. (Definitivamente, eu usaria um elenco reto, e não "como" depois de um é, btw).Se a conversão falhar, a palavra-chave 'as' não gera uma exceção; ele define a variável como nula (ou seu valor padrão para tipos de valor).
fonte
Esta não é uma resposta para a pergunta, mas comente o exemplo de código da pergunta:
Normalmente, você não precisa converter um objeto de, por exemplo, IMyInterface para MyClass. O melhor das interfaces é que, se você pegar um objeto como entrada que implementa uma interface, não precisará se importar com o tipo de objeto que está recebendo.
Se você converter IMyInterface para MyClass, já presume que obtém um objeto do tipo MyClass e não faz sentido usar IMyInterface, porque se você alimentar seu código com outras classes que implementam IMyInterface, ele quebrará seu código ...
Agora, meu conselho: se suas interfaces forem bem projetadas, você poderá evitar muitas rotulagens.
fonte
O
as
operador pode ser usado apenas em tipos de referência, não pode ser sobrecarregado e retornaránull
se a operação falhar. Isso nunca lançará uma exceção.A transmissão pode ser usada em qualquer tipo compatível, pode ser sobrecarregada e gerará uma exceção se a operação falhar.
A escolha de qual usar depende das circunstâncias. Principalmente, é uma questão de se você deseja lançar uma exceção em uma conversão com falha.
fonte
Minha resposta é apenas sobre velocidade nos casos em que não verificamos o tipo e não verificamos nulos após a transmissão. Adicionei dois testes adicionais ao código de Jon Skeet:
Resultado:
Não tente se concentrar na velocidade (como eu fiz) porque tudo isso é muito, muito rápido.
fonte
as
conversão (sem a verificação de erros) era cerca de 1-3% mais rápida que a conversão (cerca de 540ms versus 550ms em 100 milhões de iterações). Nem fará nem interromperá seu aplicativo.Além de tudo o que já foi exposto aqui, me deparei com uma diferença prática que acho que vale a pena notar, entre elenco explícito
versus usar o
as
operador.Aqui está o exemplo:
Conclusão: GenericCaster2 não funcionará com tipos de estrutura. GenericCaster irá.
fonte
Se você usar os PIAs do Office visando o .NET Framework 4.X você deve usar o como palavra-chave, caso contrário não irá compilar.
A transmissão está correta quando o .NET 2.0 é direcionado:
Ao direcionar o .NET 4.X, os erros são:
erro CS0656: O compilador ausente exigia o membro 'Microsoft.CSharp.RuntimeBinder.Binder.Convert'
erro CS0656: O compilador ausente exigia o membro 'Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create'
fonte
A
as
palavra-chave funciona da mesma forma que uma conversão explícita entre tipos de referência compatíveis, com a principal diferença de que não gera uma exceção se a conversão falhar. Em vez disso, gera um valor nulo na variável de destino. Como as exceções são muito caras em termos de desempenho, é considerado um método muito melhor de transmissão.fonte
O que você escolhe depende muito do que é necessário. Eu prefiro elenco explícito
porque se o objeto deve do tipo IMyInterface e não é - é definitivamente um problema. É melhor obter o erro o mais cedo possível, porque o erro exato será corrigido em vez de corrigir o efeito colateral.
Mas se você lida com métodos que aceitam
object
como parâmetro, precisa verificar seu tipo exato antes de executar qualquer código. Nesse caso,as
seria útil para que você possa evitarInvalidCastException
.fonte
Depende, você deseja verificar a nulidade depois de usar "como" ou prefere que seu aplicativo lance uma exceção?
Minha regra geral é se eu sempre espero que a variável seja do tipo que estou esperando no momento em que quero usar uma conversão. Se for possível que a variável não seja convertida para o que eu quero e estou preparado para lidar com nulos de usar como, usarei como.
fonte
Dê uma olhada nestes links:
eles mostram alguns detalhes e testes de desempenho.
fonte
O problema do OP é limitado a uma situação de elenco específica. O título cobre muito mais situações.
Aqui está uma visão geral de todas as situações relevantes de elenco que atualmente posso pensar:
fonte