Operadores as e is no C#
Os operadores is e as do C# ajudam a testar tipos e converter objetos com menos linhas de código e muito mais legibilidade. Neste guia, você vai entender como cada um funciona, quando usar cada operador e quais armadilhas evitar no dia a dia.
O que são os operadores is e as no C#
Os operadores is e as pertencem à família dos operadores de teste de tipo e conversão da linguagem. Além deles, essa família inclui o typeof e as expressões de cast, como (T)valor.
Em resumo, o is responde a uma pergunta simples: o valor em tempo de execução é compatível com determinado tipo? Por outro lado, o as tenta converter o valor para um tipo de referência e devolve null quando a conversão não é possível.
Na prática, os dois substituem trechos verbosos que usam GetType, typeof e casts com parênteses. Por isso, eles aparecem com frequência em código de domínio, em validações e em tratamentos de nulos.
Operadores is e as: um pouco de história
Muita gente associa os operadores is e as às versões recentes do C#, mas eles existem desde a primeira versão da linguagem. De fato, o que mudou ao longo do tempo foi o poder do is, que ganhou pattern matching.
No C# 7, o is passou a aceitar padrões, como a declaração de variável (obj is Passport passport) e o padrão constante is null. Em seguida, o C# 9 trouxe os padrões lógicos and, or e not, o que permitiu escrever is not null.
Já o C# 10 ampliou os padrões de propriedade, permitindo acessar propriedades aninhadas dentro do próprio padrão. Ou seja, cada versão tornou os operadores is e as mais expressivos, sem quebrar o código antigo.
Você pode conferir essa evolução no histórico de versões do C# no Microsoft Learn. Além disso, a documentação oficial detalha cada padrão suportado pela linguagem.
Como funciona o operador is
O is é um operador de comparação que verifica se o tipo de um valor em tempo de execução é compatível com outro tipo. Ele funciona tanto com tipos primitivos quanto com tipos complexos, como classes e records.
O resultado é sempre um bool, e essa é a primeira diferença entre os operadores is e as. Assim, você pode usar a expressão diretamente em um if, em um return ou em qualquer lugar que espere um valor lógico.
Substituindo o typeof pelo is
Antes de adotar o is, muitos desenvolvedores comparavam tipos com GetType e typeof. Veja o exemplo abaixo.
var someText = "This is a string";
// Retorna um bool
var result = someText.GetType() == typeof(string);
Nesse trecho, o GetType obtém o tipo da variável e, em seguida, o código compara esse tipo com typeof(string). Como retorno, temos um bool que informa se a variável é ou não do tipo comparado.
Embora a expressão seja simples, o is deixa o código ainda mais direto e elegante.
var result = someText is string;
No entanto, existe uma diferença importante entre as duas abordagens. O typeof compara o tipo exato, enquanto o is também aceita tipos derivados e interfaces implementadas.
object animal = new Giraffe();
Console.WriteLine(animal is Animal); // True
Console.WriteLine(animal.GetType() == typeof(Animal)); // False
public class Animal { }
public class Giraffe : Animal { }
Portanto, escolha o is quando a herança importa e o typeof quando você precisa do tipo exato.
Comparando tipos complexos com os operadores is e as
Os operadores is e as também funcionam com tipos complexos, como class e record. Para exemplificar, vamos criar alguns registros que representam documentos brasileiros, como CPF, CNPJ e passaporte.
// Documento base e seus tipos derivados
public record Document;
public record Cpf : Document;
public record Cnpj : Document;
public record Passport : Document;
Note que Cpf, Cnpj e Passport derivam de um record base chamado Document. Se essa parte ficou confusa, confira o nosso curso de orientação a objetos com C#, que explica herança em detalhes.
var passport = new Passport();
if (passport is Document)
Console.WriteLine("Passport is a document");
Da mesma forma, a linha passport is Document aplica a comparação ao objeto e retorna true. Isso acontece porque Passport herda de Document, então o tipo é compatível.
Em outras palavras, o código fica muito mais legível do que uma cadeia de comparações com typeof.
Verificando nulos com is null e is not null
Talvez o cenário mais comum para o is seja a comparação de objetos com null. Primeiro, observe um código tradicional, sem o uso do operador.
if (student == null)
Console.WriteLine("Null");
if (student != null)
Console.WriteLine("Not null");
Esse mesmo código funciona aplicando is null ou is not null na comparação. A mudança parece sutil, mas a leitura fica bem mais natural.
if (student is null)
Console.WriteLine("Null");
if (student is not null)
Console.WriteLine("Not null");
Além da legibilidade, existe uma vantagem técnica relevante. Segundo a documentação, o compilador garante que x is null nunca chama um operador == sobrecarregado pelo usuário.
Ou seja, se uma classe redefinir o == de forma inesperada, o is null continua testando a referência real. Para entender melhor o tema de nulos, leia também o artigo sobre nullable types no .NET.
Pattern matching com o operador is
Desde o C# 7, o is pode testar o tipo e declarar uma variável no mesmo passo. Esse recurso se chama padrão de declaração, e é um dos motivos pelos quais os operadores is e as ficaram tão populares.
object document = new Cpf();
if (document is Cpf cpf)
{
// cpf já está tipado como Cpf aqui dentro
Console.WriteLine($"Valid document: {cpf}");
}
Com isso, você evita um cast extra depois do teste. Além disso, o is aceita padrões relacionais e lógicos, como no exemplo abaixo.
static bool IsBusinessHour(int hour) => hour is >= 8 and < 18;
Na prática, esse estilo substitui várias comparações encadeadas com && e ||.
Padrões de propriedade com o operador is
O is também consegue testar propriedades de um objeto dentro do próprio padrão. A documentação oficial mostra um exemplo com DateTime que verifica mês, dia e dia da semana de uma vez.
static bool IsFirstFridayOfOctober(DateTime date) =>
date is { Month: 10, Day: <= 7, DayOfWeek: DayOfWeek.Friday };
A partir do C# 10, você ainda pode acessar propriedades aninhadas com a sintaxe de ponto. Esse recurso se chama padrão de propriedade estendido.
public record Point(int X, int Y);
public record Segment(Point Start, Point End);
// Verifica se alguma ponta do segmento está no eixo X
static bool IsAnyEndOnXAxis(Segment segment) =>
segment is { Start.Y: 0 } or { End.Y: 0 };
Além disso, o padrão vazio is { } corresponde a qualquer valor não nulo. Por isso, ele também serve para checar nulos e declarar uma variável ao mesmo tempo.
Operadores is e as com interfaces
Os operadores is e as não se limitam a classes e records, porque também funcionam com interfaces. Esse uso aparece bastante quando um método recebe uma abstração genérica e quer aproveitar um recurso extra.
IEnumerable<int> numbers = new List<int> { 10, 20, 30 };
// Tenta usar o acesso por índice quando disponível
if (numbers is IList<int> list)
Console.WriteLine(list[0] + list[list.Count - 1]); // 40
Nesse exemplo, a variável tem o tipo estático IEnumerable<int>, mas o objeto real é uma List<int>. Então, o is confirma que existe acesso por índice e libera o uso do list[0].
O mesmo vale para tipos de valor e boxing. Por exemplo, um int atende ao teste is IFormattable, porque o tipo implementa essa interface.
int quantity = 27;
Console.WriteLine(quantity is IFormattable); // True
object boxed = quantity;
Console.WriteLine(boxed is int); // True
Console.WriteLine(boxed is long); // False
Repare que o teste com long retorna false. Isso acontece porque o is não considera conversões numéricas, apenas conversões de identidade, referência, boxing e unboxing.
Como funciona o operador as
O as é outro operador útil, porque faz conversões explícitas de objetos de forma segura. É comum ver o tipo entre parênteses na frente da variável para fazer a conversão.
Document document = new Cpf();
var cpf = (Cpf)document;
Por outro lado, você pode escrever a mesma conversão de forma mais elegante usando as no lugar de (Cpf).
Document document = new Cpf();
var cpf = document as Cpf;
Cast explícito versus as
Apesar da semelhança visual, o cast e o as não se comportam da mesma forma quando a conversão falha. O cast lança uma InvalidCastException, enquanto o as simplesmente retorna null.
Document document = new Passport();
var cpf = document as Cpf; // cpf recebe null
var other = (Cpf)document; // lança InvalidCastException
Por isso, sempre verifique o resultado do as antes de usá-lo. Caso contrário, a falha silenciosa vira uma NullReferenceException algumas linhas depois.
var cpf = document as Cpf;
if (cpf is not null)
Console.WriteLine("Conversion succeeded");
De acordo com a documentação oficial, a expressão E as T produz o mesmo resultado que E is T ? (T)(E) : (T)null. A diferença é que o compilador avalia E apenas uma vez.
Limitações do operador as
O as considera apenas conversões de referência, conversões para tipos anuláveis e operações de boxing e unboxing. Portanto, ele não funciona com tipos de valor que não aceitam null, como int puro.
object boxed = 42;
var number = boxed as int?; // funciona, número anulável
// var invalid = boxed as int; // erro de compilação
Além disso, o as ignora conversões definidas pelo usuário com explicit operator ou implicit operator. Nesse caso, apenas o cast tradicional consegue executar a conversão.
Outra limitação aparece em métodos genéricos. Como o as pode devolver null, o compilador exige que o parâmetro de tipo seja um tipo de referência, por meio da restrição class.
// Sem a restrição class, o compilador rejeita o uso do as
public static T? TryConvert<T>(object value) where T : class
=> value as T;
Já o is não tem essa exigência, então ele funciona com qualquer parâmetro de tipo. Por isso, em código genérico, o padrão value is T typed costuma ser a alternativa mais flexível entre os operadores is e as.
Operadores is e as juntos: quando usar cada um
Os operadores is e as resolvem problemas parecidos, mas cada um brilha em uma situação. Primeiro, use o is simples quando você só precisa de um bool para decidir um fluxo.
Em seguida, prefira o is com declaração de variável quando você precisa testar e usar o objeto convertido. Esse padrão substitui a dupla as mais checagem de nulo em grande parte dos casos.
// Antes: as seguido de checagem de nulo
var cpf = document as Cpf;
if (cpf != null)
Validate(cpf);
// Depois: is com declaração de variável
if (document is Cpf validCpf)
Validate(validCpf);
Por fim, entre os operadores is e as, use o as quando o valor convertido pode legitimamente ser null e o restante do código já trata esse caso. Um exemplo típico é o encadeamento com o operador ?. ou com o ??.
var number = (document as Cpf)?.Number ?? "unknown";
Já o cast tradicional continua útil quando a conversão precisa funcionar e os operadores is e as não fazem sentido. Nesse cenário, uma exceção imediata indica um bug real e ajuda a encontrar o problema mais cedo.
Exemplo completo com documentos
Para fechar a parte prática, vamos juntar os conceitos em um pequeno serviço de validação. O método recebe um Document genérico e decide o que fazer conforme o tipo real.
public record Document;
public record Cpf(string Number) : Document;
public record Cnpj(string Number) : Document;
public record Passport(string Number, string Country) : Document;
public static class DocumentValidator
{
public static string Describe(Document? document)
{
// Checagem de nulo sem depender do operador ==
if (document is null)
return "No document provided";
// Teste de tipo com declaração de variável
if (document is Cpf cpf)
return $"CPF {cpf.Number}";
// Padrão de propriedade combinado com tipo
if (document is Passport { Country: "BR" } passport)
return $"Brazilian passport {passport.Number}";
// Conversão segura que pode resultar em null
var cnpj = document as Cnpj;
return cnpj?.Number ?? "Unknown document";
}
}
Nesse exemplo, cada técnica aparece no lugar em que faz mais sentido. Primeiro, o is null protege a entrada; em seguida, o padrão de declaração trata o CPF sem cast extra.
Depois, o padrão de propriedade filtra apenas passaportes brasileiros. Por fim, o as com ?. e ?? mostra um caso em que o null faz parte do fluxo esperado.
Com isso, você vê os operadores is e as trabalhando juntos em um código curto, legível e sem exceções inesperadas.
Erros comuns com os operadores is e as
Um erro frequente com os operadores is e as é testar o tipo com is e, logo depois, fazer um cast manual. Assim, o runtime verifica o tipo duas vezes sem necessidade.
// Evite: dois testes de tipo
if (document is Cpf)
{
var cpf = (Cpf)document;
}
// Prefira: um único teste
if (document is Cpf cpf)
{
// use cpf
}
Outro erro comum é usar o as e esquecer a checagem de nulo. Nesse caso, a falha aparece longe da origem, o que dificulta o diagnóstico.
Também vale lembrar que o is retorna false para valores nulos. Então, null is Document nunca resulta em true, mesmo que a variável tenha sido declarada como Document.
Por último, cuidado com a herança ao trocar typeof por is. Se a regra de negócio exige o tipo exato, o is pode aceitar classes filhas que você não esperava.
Boas práticas com os operadores is e as
Para tirar proveito dos operadores is e as, adote algumas práticas simples no seu time. Em primeiro lugar, padronize is null e is not null para checagens de nulo.
Em segundo lugar, prefira pattern matching ao par as mais if, porque o código fica mais curto e o escopo da variável fica mais claro. Além disso, analisadores do Visual Studio e do Roslyn costumam sugerir essa troca automaticamente.
Também é importante reservar o as para cenários em que o null faz parte do fluxo esperado. Dessa forma, quem lê o código entende que a conversão pode falhar sem que isso seja um erro.
Por fim, combine os operadores is e as com o recurso de nullable reference types. Com isso, o compilador ajuda a identificar pontos em que um resultado do as pode ser nulo e ainda não foi tratado.
Se você quiser se aprofundar nas mudanças recentes sobre nulos na linguagem, veja o artigo sobre as mudanças no null do .NET 10 e C# 14. Para conversões de texto em números, o artigo sobre Parse e TryParse no C# complementa bem este conteúdo.
Conclusão
Os operadores is e as são simples, mas melhoram muito o código, porque entregam mais legibilidade e facilitam o entendimento. O is testa tipos, checa nulos e ainda declara variáveis com pattern matching.
Enquanto isso, o as converte objetos sem lançar exceções e devolve null quando a conversão falha. Portanto, entender a diferença entre os operadores is e as e o cast tradicional evita bugs silenciosos e torna suas intenções claras para o time.
Agora, revise um trecho do seu projeto que ainda usa typeof ou casts encadeados. Em seguida, aplique os padrões deste guia e compare a legibilidade antes e depois da mudança.
FAQ
Os operadores is e as surgiram no C# 10?
Não, os dois existem desde a primeira versão do C#. As versões 7, 9 e 10 apenas ampliaram o pattern matching do operador is.
Qual a diferença entre as e cast no C#?
O cast lança InvalidCastException quando a conversão falha. Já o as retorna null e nunca lança exceção.
Posso usar as com int ou outros tipos de valor?
Somente com tipos anuláveis, como int?. Com um int puro, o compilador gera um erro.
Por que preferir is null em vez de == null?
O is null sempre testa a referência real. Por outro lado, o == pode ter uma sobrecarga que altera o resultado.
O is considera herança?
Sim, o is retorna true para tipos derivados e interfaces implementadas. Para checar o tipo exato, use GetType() == typeof(T).