Padrões de tipo

Dica

Este artigo faz parte da seção Conceitos Básicos para desenvolvedores que já conhecem pelo menos uma linguagem de programação e estão aprendendo C#. Comece com a visão geral de correspondência de padrões se os padrões forem novos para você. Para obter as regras completas de compatibilidade, consulte padrões de declaração e de tipo na referência da linguagem.

Um padrão de tipo é aplicado a uma expressão de entrada. O C# avalia a expressão e testa se o valor resultante não é nulo e seu tipo de tempo de execução é compatível com o tipo especificado. Um padrão de tipo relata apenas se o teste de tipo é bem-sucedido. Ele não declara uma variável.

Fazer uma pergunta de tipo sim-or-no

Em um sistema de entrega, IRouteStop é um contrato de capacidade para destinos aceitos pelo planejamento de rotas. CanRoute testa se o destino avaliado tem essa funcionalidade e seu resultado booliano determina se o destino entra no fluxo de trabalho de planejamento de rotas. O fluxo de trabalho precisa apenas de uma resposta de sim ou não e não lê nenhum membro route-stop, portanto um padrão de tipo sem variável capturada é adequado.

static bool CanRoute(object? destination) =>
    destination is IRouteStop;

A expressão de entrada é destinatione IRouteStop é o tipo que está sendo testado. Escolha um padrão de tipo quando a resposta for apenas sim ou não. Se o ramo correspondente precisar ler um endereço ou acessar outro membro por meio de IRouteStop, escolha um padrão de declaração para que o ramo tenha uma variável desse tipo.

Note

Você também pode ver destination is IRouteStop _. Essa sintaxe é um padrão de declaração no qual significa que _ nenhuma variável é retida. Ele executa o mesmo teste de tipo quando ambas as formas são válidas, mas destination is IRouteStop expressa mais diretamente a intenção de apenas testar.

Combinar classes e interfaces

No sistema de entrega, IRouteStop define o contrato de funcionalidade para destinos de planejamento de rotas, enquanto RouteStop é uma classe base que fornece dados comuns de parada de rota e implementação. ExpressRouteStop é uma classe especializada derivada de RouteStop. O exemplo testa um valor avaliado para que sua saída possa demonstrar todos os três tipos compatíveis. Cada teste precisa apenas de um resultado booliano, portanto, nenhuma variável capturada é necessária.

interface IRouteStop { }

abstract class RouteStop(string street) : IRouteStop
{
    public string Street { get; } = street;

    public string GetDisplayName() => Street;
}

sealed class ExpressRouteStop(string street) : RouteStop(street)
{
}

static void ShowCompatibility()
{
    object destination = new ExpressRouteStop("8 Oak Avenue");

    Console.WriteLine($"Exact class: {destination is ExpressRouteStop}");
    Console.WriteLine($"Base class: {destination is RouteStop}");
    Console.WriteLine($"Interface: {destination is IRouteStop}");
}

O valor avaliado é um ExpressRouteStop. A saída mostra que corresponde exatamente à sua classe, à classe base RouteStop e à interface IRouteStop que RouteStop implementa.

Para obter regras detalhadas de compatibilidade e casos extremos, consulte a referência de padrões de tipo.

Rotear vários tipos

Em um sistema de suporte, SupportRequest é a classe base para classes de solicitação especializadas, como PasswordResetRequest e BillingQuestion. Um braço de opção emparelha um padrão com o resultado a ser retornado quando esse padrão corresponder. Cada braço retorna o nome de uma fila de processamento e o braço final fornece uma fila de fallback para outros valores avaliados. A fila selecionada depende apenas do tipo de tempo de execução do valor avaliado, portanto, os padrões de tipo se ajustam porque nenhum membro da solicitação é lido.

static string RouteRequest(object request) =>
    request switch
    {
        PasswordResetRequest => "Identity queue",
        BillingQuestion => "Billing queue",
        SupportRequest => "General support queue",
        _ => "Intake queue"
    };

Cada braço responde a uma consulta de tipo e retorna a fila que processa essa solicitação. Se um braço precisar ler os membros da solicitação, use um padrão de declaração para capturar o valor correspondente em uma variável.

Os braços de comutação são considerados de cima para baixo. Coloque uma classe especializada antes de sua classe base. Caso contrário, o braço de classe base pode corresponder a todas as instâncias da classe especializada, o que torna o braço posterior inacessível.

Opcional: usar um parâmetro de tipo como o tipo testado

Este exemplo opcional baseia-se em tipos e métodos genéricos. Um parâmetro de tipo , como TRequest é um espaço reservado para um tipo fornecido pelo chamador. Um lote de solicitação de entrada pode conter vários tipos de solicitação e o chamador fornece ConfidentialRequest para testar se qualquer solicitação requer tratamento confidencial. O resultado booliano seleciona o tratamento confidencial para todo o lote e produz uma mensagem de status visível. Um padrão de tipo com um parâmetro de tipo se encaixa porque apenas a existência de uma solicitação correspondente importa, portanto, o objeto correspondente não precisa ser retido.

static void ShowConfidentialBatchHandling()
{
    object[] incomingRequests = [new BillingQuestion(), new ConfidentialRequest()];
    bool requiresConfidentialHandling =
        ContainsRequestOfType<ConfidentialRequest>(incomingRequests);

    Console.WriteLine(requiresConfidentialHandling
        ? "Send the entire batch to confidential handling."
        : "Send the batch to standard handling.");
}

static bool ContainsRequestOfType<TRequest>(IEnumerable<object> requests)
{
    foreach (object request in requests)
    {
        if (request is TRequest)
        {
            return true;
        }
    }

    return false;
}

Se o chamador precisasse da solicitação correspondente em si, uma operação de pesquisa ou filtro que retornasse itens correspondentes seria mais apropriada.

Consulte também