Direct casting vs ‘como’ operador?

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ê tem a intenção). Qual deve ser o preferido?

 string s = (string)o; // 1 

Lança InvalidCastException se o não for uma string . Caso contrário, atribui o a s , mesmo se o for null .

 string s = o as string; // 2 

Atribui null para s se o não for uma string ou se o for 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 a s .

 string s = o.ToString(); // 3 

Faz com que um NullReferenceException se o é null . Atribui o que o.ToString() retorna para s , não importa o tipo o é.


Use 1 para a maioria das conversões – é simples e direto. Eu costumo quase nunca usar 2 desde que se algo não é o tipo certo, eu geralmente espero que uma exceção ocorra. Eu só vi uma necessidade para esse tipo de funcionalidade return-null com bibliotecas mal projetadas que usam códigos de erro (por exemplo, retornar null = error, em vez de usar exceções).

3 não é um casting e é apenas uma invocação de método. Use-o para quando precisar da representação de string de um object não-string.

  1. Use quando algo definitivamente deveria ser a outra coisa.
  2. Use quando algo pode ser a outra coisa.
  3. Use quando você não se importa com o que é, mas apenas deseja usar a representação de string disponível.

Isso realmente depende se você sabe se o é uma string e o que você quer fazer com ela. Se o seu comentário significa que realmente é uma string, eu prefiro o straight (string)o – é improvável que ele falhe.

A maior vantagem de usar o casting direto é que, quando ele falha, você recebe um InvalidCastException , que informa muito bem o que deu errado.

Com o operador as, se o não for uma string, s será definido como 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 NullReferenceException lançado. Estes tendem a ser mais comuns e muito mais difíceis de rastrear uma vez que acontecem na natureza, já que quase todas as linhas desreferenciam uma variável e podem lançar uma. Por outro lado, se você está tentando converter para um tipo de valor (qualquer primitiva ou estruturas como DateTime ), você tem que usar o método direto – o as não funciona.

No caso especial de conversão para uma string, todo object tem um ToString , então seu terceiro método pode estar ok se o não for null e você achar que o método ToString pode fazer o que você quer.

Se você já sabe para qual tipo pode converter, use um modelo de estilo C:

 var o = (string) iKnowThisIsAString; 

Observe que somente com um estilo C você pode executar coerção de tipo explícito.

Se você não sabe se é o 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. Só será não nulo se o object não for nulo e nativamente do tipo especificado.

Use ToString () para obter uma representação de string legível por humanos de qualquer object, mesmo que não seja possível converter para string.

A palavra-chave as é boa em 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, em seguida, ter que lançá-la do object como faria com uma conversão direta:

 object linkObj = this.FindControl("linkid"); if (link != null) { Hyperlink link = (Hyperlink)linkObj; } 

Não é uma coisa enorme, mas economiza linhas de código e atribuição de variables, além de ser mais legível

‘as’ é baseado em ‘is’, que é uma palavra-chave que verifica em tempo de execução se o object é compatível polimorficamente (basicamente, se uma conversão pode ser feita) e retorna null se a verificação falhar.

Estes dois são equivalentes:

Usando ‘as’:

 string s = o as string; 

Usando ‘is’:

 if(o is string) s = o; else s = null; 

Pelo contrário, a conversão de estilo c é feita também em tempo de execução, mas lança uma exceção se o lançamento não puder ser feito.

Apenas para adicionar 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ê tem que usar o casting.

2 é útil para transmitir para um tipo derivado.

Suponha que a é um animal:

 b = a as Badger; c = a as Cow; if (b != null) b.EatSnails(); else if (c != null) c.EatGrass(); 

vai receber um feed com um mínimo de castings.

“(string) o” resultará em InvalidCastException, pois não há transmissão direta.

“o as string” resultará em s sendo uma referência nula, em vez de uma exceção ser lançada.

“o.ToString ()” não é um casting de qualquer tipo per se, é um método que é implementado por object e, portanto, de uma forma ou de outra, por todas as classs em .net que “faz alguma coisa” com a instância de a class é chamada e retorna uma string.

Não esqueça que para converter em string, também há Convert.ToString (someType instanceOfThatType) onde someType é um de um conjunto de tipos, essencialmente os tipos básicos de frameworks.

De acordo com experimentos executados nesta página: http://www.dotnetguru2.org/sebastienros/index.php/2006/02/24/cast_vs_as

(esta página está tendo alguns erros de “referenciadores ilegais” às vezes, então apenas atualize se isso acontecer)

A conclusão é que o operador “as” é normalmente mais rápido que um lançamento. Às vezes, muitas vezes mais rápido, às vezes apenas um pouco mais rápido.

Eu peronsonally coisa “como” também é mais legível.

Então, como é mais rápido e “mais seguro” (não há exceção) e possivelmente mais fácil de ler, recomendo usar “as” o tempo todo.

Todas as respostas dadas são boas, se eu puder adicionar algo: Para usar diretamente os methods e propriedades da string (por exemplo, ToLower) você não pode escrever:

 (string)o.ToLower(); // won't compile 

você só pode escrever:

 ((string)o).ToLower(); 

mas você pode escrever:

 (o as string).ToLower(); 

A opção as é mais legível (pelo menos na minha opinião).

 string s = o as string; // 2 

É preferível, pois evita a penalidade de desempenho do double casting.

Parece que os dois são conceitualmente diferentes.

Fundição Direta

Tipos não precisam estar estritamente relacionados. Vem em todos os tipos de sabores.

  • Vazamento implícito / explícito personalizado: geralmente, um novo object é criado.
  • Tipo de Valor Implícito: Copie sem perder informações.
  • Tipo de Valor Explícito: Cópia e informação podem ser perdidas.
  • Relação IS-A: Alterar o tipo de referência, caso contrário, lança uma exceção.
  • O mesmo tipo: “A transmissão é redundante”.

Parece que o object será convertido em outra coisa.

Operador AS

Tipos têm um relacionamento direto. Como em:

  • Tipos de referência: relacionamento IS-A Os objects são sempre os mesmos, apenas as alterações de referência.
  • Tipos de valor: Copie os tipos boxe e nullable.

Parece que você vai lidar com o object 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::.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 } 

Ao tentar obter a representação de cadeia de qualquer coisa (de qualquer tipo) que poderia ser potencialmente nula, prefiro a linha de código abaixo. Ele é compacto, chama ToString () e manipula nulos corretamente. Se o for nulo, s conterá String.Empty.

 String s = String.Concat(o); 

Como ninguém mencionou, o mais próximo de instanceOf to Java por palavra-chave é o seguinte:

 obj.GetType().IsInstanceOfType(otherObj) 

Use string s = (string) o; casting direto string s = (string) o; se no contexto lógico da sua string aplicativo for o único tipo válido. Com essa abordagem, você receberá InvalidCastException e implementará o princípio de fail-fast . Sua lógica será protegida de passar o tipo inválido ainda ou obter NullReferenceException se usado as operador.

Se a lógica espera que vários tipos diferentes usem string s = o as string; e verifique isso em null ou use is operator.

Novo recurso legal apareceu no C # 7.0 para simplificar o lançamento e a verificação é uma 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 seguintes duas formas de conversão de tipo (conversão) são suportadas em C #:

|

(Cv

• Converta o tipo estático de v para c na expressão especificada

• Somente possível se o tipo dynamic de v for c, ou um subtipo de c

• Se não, uma InvalidCastException é lançada

|

v como C

• Variante não fatal de (c) v

• Assim, converta o tipo estático de v para c na expressão dada

• Retorna null se o tipo dynamic de v não for c, ou um subtipo de c