Fundição direta vs operador 'as'?

710

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 elenco (ok, o terceiro não é um elenco, mas você tem a intenção). Qual deles deve ser preferido?

nullDev
fonte
1
Não é uma duplicata, mas também há algumas discussões sobre desempenho em uma pergunta anterior .
Unsliced
8
string s = Convert.ToString(o):; 5º: string s = $"{o}"(ou equivalentemente o string.Formatformulário para C # anterior)
Earth Engine

Respostas:

834
string s = (string)o; // 1

Lança InvalidCastException se onão for um string. Caso contrário, atribui oa s, mesmo que oseja null.

string s = o as string; // 2

Atribui nulla sse onão é um stringou se oé null. Por esse motivo, você não pode usá-lo com tipos de valor (o operador nunca poderá retornar nullnesse caso). Caso contrário, atribui oa s.

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

Causa uma NullReferenceException se ofor null. Atribui o que quer que o.ToString()retorne s, independentemente do tipo o.


Use 1 para a maioria das conversões - é simples e direto. Costumo quase nunca usar 2, pois se algo não for do tipo certo, normalmente espero que ocorra uma exceção. Eu só vi a necessidade desse tipo de funcionalidade de retorno nulo com bibliotecas mal projetadas que usam códigos de erro (por exemplo, retorno 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 você precisar da representação em sequência de um objeto que não seja sequência.

Sander
fonte
2
Você pode atribuir 'null' a tipos de valor quando definidos explicitamente, por exemplo: int? Eu; string s = "5"; i = s como int; // i agora é 5 s = nulo; i = s como int; // agora é nulo
Anheledir 25/09/08
3
RE: Anheledir Na verdade, eu seria nulo após a primeira chamada. Você precisa usar uma função de conversão explícita para obter o valor de uma string.
Guvante 25/09/08
45
RE: Sander Na verdade, há outro motivo muito bom para usar, pois simplifica seu código de verificação (verifique se há um valor nulo em vez de um tipo nulo e correto) Isso é útil, pois muitas vezes você prefere lançar uma exceção personalizada. Mas é bem verdade que as cegas são ruins.
Guvante 25/09/08
5
# 2 é útil para coisas como métodos Equals, onde você não conhece o tipo de entrada. Geralmente, sim, 1 seria o preferido. Embora preferência sobre que seria, obviamente, usando o sistema de tipo de restringir a um tipo quando você só esperar um :)
Calum
6
O nº 2 também é útil quando você possui um código que pode fazer algo específico para um tipo especializado, mas que não faria nada.
AnthonyWJones
349
  1. string s = (string)o;Use quando algo definitivamente deve ser a outra coisa.
  2. string s = o as string;Use quando algo puder ser a outra coisa.
  3. string s = o.ToString(); Use quando você não se importa com o que é, mas você apenas deseja usar a representação de string disponível.
Quibblesome
fonte
1
Sinto que essa resposta parece boa, mas pode não ser precisa.
j riv
1
Eu gosto dos dois primeiros, mas gostaria de acrescentar "e você tem certeza de que não é nulo" à terceira opção.
Uxonith
2
você pode usar Elvis (?.) hoje em dia para evitar ter que se preocupar com isso: obj? .ToString ()
Quibblesome
@ Quibblesome - ótima resposta, mas eu tive que parar para pensar na sua refutação! literalmente, me surpreende que a linguagem exista há mais de 15 anos. Parece que foi ontem quando estávamos sendo "nervosos" tentando convencer os desenvolvedores seniores a mudar para o C #.
Griswald_911
1
@ Resposta agradável e curiosa: você ficará aborrecido se eu adicionar o que é 1/2/3 para que não seja necessário rolar até OP. Eu com SO classificaria respostas antigas de acordo com os votos!
whytheq
29

Realmente depende se você sabe se oé uma string e o que deseja fazer com ela. Se seu comentário significa que orealmente é uma sequência, eu prefiro o (string)oelenco direto - é improvável que falhe.

A maior vantagem de usar a conversão direta é que, quando falha, você recebe uma InvalidCastException , que informa praticamente o que deu errado.

Com o asoperador, se onão for uma sequência, sestá definido como null, o que é útil se você não tiver certeza e quiser 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, usará smais tarde e lançará uma NullReferenceException . Eles tendem a ser mais comuns e muito mais difíceis de rastrear quando ocorrem na natureza, pois quase todas as linhas referenciam uma variável e podem jogar uma. Por outro lado, se você estiver tentando converter para um tipo de valor (qualquer primitivo ou estrutura como DateTime ), precisará usar a conversão direta - asisso não funcionará.

No caso especial de conversão para uma string, todo objeto possui um ToString, portanto seu terceiro método pode ser bom se onão for nulo e você acha que o ToStringmétodo pode fazer o que você deseja.

Blair Conrad
fonte
2
Uma observação - você pode usar ascom tipos de valores anuláveis . IE o as DateTimenão vai funcionar, mas o as DateTime?vai ...
John Gibb
Por que não usar if (s is string)?
fácil
1
@BornToCode, para mim, preferência em grande parte pessoal. Dependendo do que você está fazendo, muitas vezes depois de ising, você terá que lançar novamente de qualquer maneira, para ter o is e depois um hard cast. Por alguma razão, a asverificação nula e me pareceu melhor.
Blair Conrad
9

Se você já sabe para qual tipo ele pode transmitir, use um estilo C:

var o = (string) iKnowThisIsAString; 

Observe que apenas com uma conversão no estilo C você pode executar coerção de tipo explícita.

Se você não sabe se é o tipo desejado e 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 as chamadas não serão chamadas para nenhum operador de conversão de tipo. Somente será não nulo se o objeto não for nulo e nativamente do tipo especificado.

Use ToString () para obter uma representação de string legível por humanos de qualquer objeto, mesmo que não possa ser convertido em string.

Mark Cidade
fonte
3
Essa é uma pequena pegadinha interessante sobre os operadores de conversão de tipo. Eu tenho alguns tipos para os quais eu criei conversões, que devem estar atentos a isso.
AnthonyWJones
7

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 a variável digitada em vez de precisar convertê-la objectcomo faria com uma conversão direta:

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 de variáveis, além de ser mais legível

Glenn Slaven
fonte
6

'as' é baseado em 'is', que é uma palavra-chave que verifica em tempo de execução se o objeto é polimorfricamente compatível (basicamente se é possível fazer uma conversão) e retorna nulo se a verificação falhar.

Estes 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 é feito também em tempo de execução, mas lança uma exceção se o elenco 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ê deve usar o casting.

Sergio Acosta
fonte
Obrigado por apontar meu erro, você está certo. Eu editei a resposta. OPA, desculpe.
Sergio Acosta
5

2 é útil para converter 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 ter um alimentado com um mínimo de moldes.

Joel em Gö
fonte
2
@Chirs Moutray, isso nem sempre é possível, especialmente se for uma biblioteca.
Deceleratedcaviar
5

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

(esta página está apresentando alguns erros de "referenciador ilegal" às vezes, então atualize apenas)

A conclusão é que o operador "as" é normalmente mais rápido que uma conversão. Às vezes, muitas vezes mais rápido, às vezes apenas mais rápido.

Eu costumo dizer que "como" também é mais legível.

Portanto, como é mais rápido e "mais seguro" (não emitirá exceção) e possivelmente mais fácil de ler, eu recomendo usar "como" o tempo todo.

Brady Moritz
fonte
4

"(string) o" resultará em InvalidCastException, pois não há conversão direta.

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

"o.ToString ()" não é um elenco de nenhum tipo per se, é um método implementado por objeto e, portanto, de uma maneira ou de outra, por todas as classes em .net que "fazem algo" com a instância de a classe em que é chamada e retorna uma string.

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

Roubar
fonte
3

Todas as respostas dadas são boas, se eu puder adicionar algo: Para usar diretamente os métodos 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ê poderia escrever:

(o as string).ToLower();

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

BornToCode
fonte
a construção (o como string) .ToLower () anula o objetivo do operador as. Isso lançará uma exceção de referência nula quando o não puder ser convertido em string.
james
@james - Mas quem disse que o único objetivo do operador as é lançar exceção se o elenco falhar? Se você souber que o é uma string e apenas quiser escrever um código mais limpo, poderá usar em (o as string).ToLower()vez dos vários colchetes confusos.
BornToCode 31/08/16
o objetivo do as é exatamente o oposto - ele não deve lançar a exceção quando o elenco falhar, deve retornar nulo. Digamos que o seu é uma string com um valor nulo, o que acontecerá então? Dica - sua ligação ao ToLower falhará.
James
@ james - Você está certo, mas e os casos em que eu tenho certeza de que não será nulo e só preciso fazer a conversão do compilador para me permitir acessar os métodos desse objeto?
BornToCode
1
você pode definitivamente fazer isso, mas não é exatamente a melhor prática, porque não deseja confiar no chamador ou nos sistemas externos para garantir que seu valor não seja nulo. Se você estiver usando C # 6, poderá fazer (o como string) ?. Abaixar().
James
3
string s = o as string; // 2

É preferível, pois evita a penalidade de desempenho da dupla fundição.

Chris S
fonte
Olá Chris, o link que estava nesta resposta agora é 404 ... Não sei se você tem um substituto que deseja colocar no lugar?
Matt
3

Parece que os dois são conceitualmente diferentes.

Fundição Direta

Os tipos não precisam ser estritamente relacionados. Ele vem em todos os tipos de sabores.

  • Conversão implícita / explícita personalizada: geralmente um novo objeto é criado.
  • Tipo de valor implícito: copie sem perder informações.
  • Tipo de valor explícito: cópia e informações podem ser perdidas.
  • Relacionamento IS-A: altere o tipo de referência, caso contrário lança exceção.
  • Mesmo tipo: 'A transmissão é redundante'.

Parece que o objeto será convertido em outra coisa.

Operador AS

Os tipos têm um relacionamento direto. Como em:

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

Parece que você vai manipular 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
    }
Lucas Teixeira
fonte
2

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 realiza 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 as definidas pelo usuário, que devem ser executadas usando expressões de conversão.

Vadim S.
fonte
0

Ao tentar obter a representação em string de qualquer coisa (de qualquer tipo) que possa ser potencialmente nula, prefiro a linha de código abaixo. É compacto, invoca ToString () e lida corretamente com nulos. Se o for nulo, s conterá String.Empty.

String s = String.Concat(o);
xtrem
fonte
0

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

obj.GetType().IsInstanceOfType(otherObj)
Bennett Yeo
fonte
0

Use conversão direta string s = (string) o;se, no contexto lógico do seu aplicativo, stringfor o único tipo válido. Com essa abordagem, você obterá InvalidCastExceptione implementará o princípio de Fail-fast . Sua lógica será protegida de passar ainda mais o tipo inválido ou obter o NullReferenceException se for utilizado as.

Se a lógica espera que vários tipos diferentes sejam expressos, string s = o as string;marque-a nullou use o isoperador.

Um novo recurso interessante apareceu no C # 7.0 para simplificar o elenco e verificar se há 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;
 }
Dmitry
fonte
0

As duas formas a seguir de conversão de tipo (conversão) são suportadas em C #:

|

(Cv

• Converta o tipo estático de v em c na expressão fornecida

• Só é possível se o tipo dinâmico de v for c ou um subtipo de c

• Caso contrário, uma InvalidCastException é lançada

|

v como C

• Variante não fatal de (c) v

• Assim, converta o tipo estático de v em c na expressão fornecida

• Retorna nulo se o tipo dinâmico de v não for c ou um subtipo de c


fonte