Padrões de tipo

Tip

Este artigo faz parte da secção Fundamentos para programadores que já conhecem pelo menos uma linguagem de programação e estão a aprender C#. Começa com a visão geral de correspondência de moldes se os moldes forem novos para ti. Para regras completas de compatibilidade, consulte padrões de declaração e tipos na referência da linguagem.

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

Faz uma pergunta do tipo simor-no

Num sistema de entrega, IRouteStop é um contrato de capacidades para destinos aceites pelo planeamento de rotas. CanRoute testa se o destino avaliado tem essa capacidade, e o seu resultado booleano determina se o destino entra no fluxo de trabalho de planeamento de rotas. O fluxo de trabalho precisa apenas de uma resposta sim ou não e não lê quaisquer membros de paragem da rota, pelo que um padrão de tipo sem variável capturada é adequado.

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

A expressão de entrada é destination, e IRouteStop é o tipo a ser testado. Escolhe um padrão de tipo quando a resposta for apenas sim ou não. Se o ramo correspondente precisar de ler um endereço ou chamar outro membro através de IRouteStop, escolha antes um padrão de declaração, para que o ramo tenha uma variável desse tipo.

Note

Também podes ver destination is IRouteStop _. Esta sintaxe é um padrão de declaração em que _ significa que nenhuma variável é retida. Realiza o mesmo teste de tipo quando ambas as formas são válidas, mas destination is IRouteStop declara a intenção apenas do teste de forma mais direta.

Classes de correspondência e interfaces

No sistema de entrega, IRouteStop define o contrato de capacidades para destinos de planeamento de percurso, enquanto RouteStop é uma classe base que fornece dados comuns de paragem de percurso e a respetiva implementação. ExpressRouteStop é uma classe especializada derivada de RouteStop. O exemplo testa um valor avaliado para que a sua saída possa demonstrar os três tipos compatíveis. Cada teste necessita apenas de um resultado booleano, pelo que não é necessária nenhuma variável capturada.

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, à sua RouteStop classe base e à IRouteStop interface que RouteStop implementa.

Para regras detalhadas de compatibilidade e casos extremos, consulte a referência do padrão de tipos.

Vários tipos de percurso

Num sistema de apoio, SupportRequest é a classe base para classes especializadas de pedidos como PasswordResetRequest e BillingQuestion. Um ramo de switch associa um padrão ao resultado a devolver quando esse padrão corresponde. Cada braço devolve o nome de uma fila de processamento, e o braço final fornece uma fila de retenção para outros valores avaliados. A fila selecionada depende apenas do tipo em tempo de execução do valor avaliado, pelo que os padrões de tipo são adequados, porque nenhum membro do pedido é lido.

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

Cada ramo responde a uma pergunta do tipo e devolve a fila que processa esse pedido. Se um braço precisar de ler membros do pedido, use um padrão de declaração para capturar o valor correspondente numa variável.

Os braços dos interruptores são considerados de cima para baixo. Coloca uma classe especializada antes da classe base. Caso contrário, o braço da 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 tipo testado

Este exemplo opcional baseia-se em tipos e métodos genéricos. Um parâmetro de tipo como TRequest é um marcador de posição para um tipo fornecido pelo chamador. Um lote de pedidos recebidos pode conter vários tipos de pedidos, e o chamador fornece ConfidentialRequest para testar se algum pedido necessita de tratamento confidencial. O resultado Booleano seleciona o tratamento confidencial para todo o lote e produz uma mensagem de estado visível. Um padrão de tipo com um parâmetro de tipo encaixa porque só a existência de um pedido correspondente importa, pelo que o objeto correspondente não precisa de 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 autor da chamada precisasse do próprio pedido correspondente, seria mais apropriada uma operação de pesquisa ou de filtragem que devolvesse itens correspondentes.

Consulte também