Casting direto vs operador 'como'?
Pergunta
Considere o seguinte código:
void Handler(object o, EventArgs e)
{
// I swear o is a string
string s = (string)o; // 1
//-OR-
string s = o as string; // 2
// -OR-
string s = o.ToString(); // 3
}
Qual é a diferença entre os três tipos de casting (ok, o terceiro não é um casting, mas você entendeu a intenção).Qual deles deve ser preferido?
Solução
string s = (string)o; // 1
Joga InvalidCastException E se o
não é um string
. Caso contrário, atribui o
para s
, ainda que o
é null
.
string s = o as string; // 2
Atribui null
para s
E se o
não é um string
ou se o
é null
. Por esse motivo, você não pode usá -lo com tipos de valor (o operador nunca poderia retornar null
nesse caso). Caso contrário, atribui o
para s
.
string s = o.ToString(); // 3
Causa a Exceção de Referência Nula E se o
é null
. Atribui o que quer que seja o.ToString()
volta para s
, não importa que tipo o
é.
Use 1 para a maioria das conversões - é simples e direto. Eu quase nunca uso 2, pois se algo não é do tipo certo, geralmente espero que ocorra uma exceção. Eu só vi uma necessidade desse tipo de funcionalidade de retorno-nulo com bibliotecas mal projetadas que usam códigos de erro (por exemplo, retornar nulo = erro, em vez de usar exceções).
3 não é um elenco e é apenas uma invocação de método. Use-o para quando precisar da representação da string de um objeto que não é de string.
Outras dicas
string s = (string)o;
Uso quando algo deve definitivamente ser a outra coisa.string s = o as string;
Uso quando algo pode ser outros coisa.string s = o.ToString();
Use quando você não se importa com o que é, mas você só quer usar o disponível representação de seqüência de caracteres.
Realmente depende se você sabe se o
é uma string e o que você quer fazer com ela. Se o seu comentário significa que o
Realmente é realmente uma corda, eu preferiria a reta (string)o
Elenco - é improvável que falhe.
A maior vantagem de usar o elenco reto é que, quando falha, você obtém um InvalidCastException, o que lhe diz praticamente o que deu errado.
Com o as
operador, se o
não é uma corda, s
está configurado para null
, o que é útil se você não tiver certeza e deseja testar s
:
string s = o as string;
if ( s == null )
{
// well that's not good!
gotoPlanB();
}
No entanto, se você não executar esse teste, você usará s
mais tarde e ter um Exceção de Referência Nula jogado. Estes tendem a ser mais comuns e um muito Mais difícil de rastrear quando eles acontecem na natureza, pois quase todas as linhas desreferências uma variável e podem jogar uma. Por outro lado, se você está tentando lançar para um tipo de valor (qualquer primitivo, ou estruturas como Data hora), você tem que usar o elenco reto - o as
não vai funcionar.
No caso especial de converter em uma string, todo objeto tem um ToString
, então seu terceiro método pode estar bem se o
não é nulo e você acha que o ToString
o método pode fazer o que você deseja.
Se você já sabe para que tipo ele pode ser lançado, use um elenco de estilo C:
var o = (string) iKnowThisIsAString;
Observe que somente com um elenco de estilo C você pode executar a coerção de tipo explícita.
Se você não sabe se é do tipo desejado e você vai usá -lo, se for, use Como palavra -chave:
var s = o as string;
if (s != null) return s.Replace("_","-");
//or for early return:
if (s==null) return;
Observe que Como não chamará nenhum operador de conversão de tipo. Somente não será nulo se o objeto não for nulo e nativamente do tipo especificado.
Use o ToString () para obter uma representação de string legível pelo homem de qualquer objeto, mesmo que ele não possa ser lançado para o String.
A palavra -chave AS é boa no ASP.NET quando você usa o método FindControl.
Hyperlink link = this.FindControl("linkid") as Hyperlink;
if (link != null)
{
...
}
Isso significa que você pode operar na variável digitada, em vez de ter que então lançá -la de object
Como você faria com um elenco direto:
object linkObj = this.FindControl("linkid");
if (link != null)
{
Hyperlink link = (Hyperlink)linkObj;
}
Não é uma coisa enorme, mas salva linhas de código e atribuição variável, além de ser mais legível
'como' é baseado em 'é', que é uma palavra -chave que verifica em tempo de execução se o objeto for compatível com polimorficalmente (basicamente se um elenco puder ser feito) e retornará nulos se a verificação falhar.
Esses dois são equivalentes:
Usando 'como':
string s = o as string;
Usando 'é':
if(o is string)
s = o;
else
s = null;
Pelo contrário, o elenco do estilo C também é feito em tempo de execução, mas lança uma exceção se o elenco não puder ser feito.
Apenas para acrescentar um fato importante:
A palavra -chave 'AS' funciona apenas com tipos de referência. Você não pode fazer:
// I swear i is an int
int number = i as int;
Nesses casos, você deve usar o elenco.
2 é útil para fundir para um tipo derivado.
Suponha uma é um animal:
b = a as Badger;
c = a as Cow;
if (b != null)
b.EatSnails();
else if (c != null)
c.EatGrass();
terá uma alimentado com um mínimo de elencos.
De acordo com as experiências executadas nesta página: http://www.dotnetguru2.org/sebastienros/index.php/2006/02/24/cast_vs_as
(Esta página está tendo alguns erros de "referenciador ilegal" aparecem às vezes, então apenas atualize se isso acontecer)
A conclusão é que o operador "As" é normalmente mais rápido que um elenco. Às vezes, muitas vezes mais rápido, às vezes apenas mais rápido.
Eu meronsonalmente "como" também é mais legível.
Portanto, como é mais rápido e "mais seguro" (não lançará exceção) e, possivelmente, mais fácil de ler, recomendo usar "como" o tempo todo.
"(String) O" resultará em uma concepção inválida, pois não há elenco direto.
"O como string" resultará em S ser uma referência nula, em vez de uma exceção sendo lançada.
"O.ToString ()" não é um elenco de qualquer tipo por se, é um método que é implementado pelo objeto e, portanto, de uma maneira ou de outra A classe é chamada e retorna uma string.
Não se esqueça que, por converter em string, também há convert.toString (algum tipo de instância do THETTYPE), onde algum tipo é um de um conjunto de tipos, essencialmente os tipos de base da estrutura.
Todas as respostas dadas são boas, se eu adicionar algo: para usar diretamente os métodos e propriedades da String (por exemplo, tolo), você não pode escrever:
(string)o.ToLower(); // won't compile
Você só pode escrever:
((string)o).ToLower();
Mas você pode escrever: em vez disso:
(o as string).ToLower();
o as
A opção é mais legível (pelo menos na minha opinião).
string s = o as string; // 2
É preferido, pois evita a penalidade de desempenho da fundição dupla.
Parece que os dois são conceitualmente diferentes.
Elenco direto
Os tipos não precisam ser estritamente relacionados. Ele vem em todos os tipos de sabores.
- Elenco implícito/explícito personalizado: Geralmente um novo objeto é criado.
- Tipo de valor implícito: Copie sem perder informações.
- Tipo de valor explícito: Cópia e informação podem ser perdidas.
- IS-A Relacionamento: Alterar o tipo de referência, caso contrário, lança exceção.
- Mesmo tipo: 'Elenco é redundante'.
Parece que o objeto será convertido em outra coisa.
Como operador
Os tipos têm um relacionamento direto. Como em:
- Tipos de referência: É um relacionamento Os objetos são sempre os mesmos, apenas as mudanças de referência.
- Tipos de valor: cópia de boxe e tipos anuláveis.
Parece que você vai lidar com o objeto de uma maneira diferente.
Amostras e il
class TypeA
{
public int value;
}
class TypeB
{
public int number;
public static explicit operator TypeB(TypeA v)
{
return new TypeB() { number = v.value };
}
}
class TypeC : TypeB { }
interface IFoo { }
class TypeD : TypeA, IFoo { }
void Run()
{
TypeA customTypeA = new TypeD() { value = 10 };
long longValue = long.MaxValue;
int intValue = int.MaxValue;
// Casting
TypeB typeB = (TypeB)customTypeA; // custom explicit casting -- IL: call class ConsoleApp1.Program/TypeB ConsoleApp1.Program/TypeB::op_Explicit(class ConsoleApp1.Program/TypeA)
IFoo foo = (IFoo)customTypeA; // is-a reference -- IL: castclass ConsoleApp1.Program/IFoo
int loseValue = (int)longValue; // explicit -- IL: conv.i4
long dontLose = intValue; // implict -- IL: conv.i8
// AS
int? wraps = intValue as int?; // nullable wrapper -- IL: call instance void valuetype [System.Runtime]System.Nullable`1<int32>::.ctor(!0)
object o1 = intValue as object; // box -- IL: box [System.Runtime]System.Int32
TypeD d1 = customTypeA as TypeD; // reference conversion -- IL: isinst ConsoleApp1.Program/TypeD
IFoo f1 = customTypeA as IFoo; // reference conversion -- IL: isinst ConsoleApp1.Program/IFoo
//TypeC d = customTypeA as TypeC; // wouldn't compile
}
Gostaria de chamar a atenção para as seguintes especificidades do como operador:
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/as
Observe que o operador AS executa apenas conversões de referência, conversões anuláveis e conversões de boxe.O operador AS não pode executar outras conversões, como conversões definidas pelo usuário, que devem ser executadas usando expressões de elenco.
Ao tentar obter a representação da string de qualquer coisa (de qualquer tipo) que possa ser potencialmente nula, prefiro a linha de código abaixo. É compacto, chama o tostring () e manipula corretamente os nulos. Se O for nulo, S conterá String.Empty.
String s = String.Concat(o);
Como ninguém mencionou, o mais próximo de Java por palavra -chave é a seguinte:
obj.GetType().IsInstanceOfType(otherObj)
Use elenco direto string s = (string) o;
Se no contexto lógico do seu aplicativo string
é o único tipo válido. Com essa abordagem, você vai conseguir InvalidCastException
e implementar o princípio de Fail-Fast. Sua lógica estará protegida de passar mais do tipo inválido ou obter o NullReferenceException se usado as
operador.
Se a lógica espera vários tipos diferentes lançados string s = o as string;
e verifique null
ou uso is
operador.
Novo recurso legal apareceu no C# 7.0 para simplificar o elenco e o check é um Correspondência de padrões:
if(o is string s)
{
// Use string variable s
}
or
switch (o)
{
case int i:
// Use int variable i
break;
case string s:
// Use string variable s
break;
}
As duas formas a seguir de conversão de tipo (fundição) são suportadas em C#:
|
(Cv
• converter o tipo estático de V em C na expressão dada
• Somente possível se o tipo dinâmico de V for C, ou um subtipo de C
• Caso contrário, uma invalidcastException é jogada
|
v como c
• Variante não fatal de (c) V
• Assim, converta o tipo estático de V em C na expressão dada
• Retorna nulo se o tipo dinâmico de V não for C, ou um subtipo de C