Partilhar via


Correspondência de padrões - as is switch e expressões, e operadores and, or e not em padrões

Use a is expressão, a instrução switch e a expressão switch para corresponder a uma expressão de entrada a qualquer número de características. O C# suporta vários padrões, incluindo declaração, tipo, constante, relacional, propriedade, lista, var e descarte. Os padrões podem ser combinados usando palavras-chave lógicas booleanas and, or, e not.

As seguintes expressões e instruções em C# oferecem suporte à correspondência de padrões:

Nessas construções, você pode fazer a correspondência de uma expressão de entrada com qualquer um dos seguintes padrões:

  • Padrão de declaração: para verificar o tipo de tempo de execução de uma expressão e, se uma correspondência for bem-sucedida, atribuir um resultado de expressão a uma variável declarada.
  • Padrão de tipo: para verificar o tipo de tempo de execução de uma expressão.
  • Padrão constante: para testar se um resultado de expressão é igual a uma constante especificada.
  • Padrões relacionais: para comparar um resultado de expressão com uma constante especificada.
  • Padrões lógicos: para testar se uma expressão corresponde a uma combinação lógica de padrões.
  • Padrão de propriedade: para testar se as propriedades ou campos de uma expressão correspondem a padrões aninhados.
  • Padrão posicional: para desconstruir um resultado de expressão e testar se os valores resultantes correspondem a padrões aninhados.
  • var pattern: para corresponder a qualquer expressão e atribuir seu resultado a uma variável declarada.
  • Descartar padrão: para corresponder a qualquer expressão.
  • Listar padrões: para testar se os elementos da sequência correspondem aos padrões aninhados correspondentes. Introduzido em C# 11.

Padrões lógicos, de propriedade, posicionais e de lista são padrões recursivos . Ou seja, eles podem conter padrões aninhados .

Para obter o exemplo de como usar esses padrões para criar um algoritmo controlado por dados, consulte Tutorial: Usar a correspondência de padrões para criar algoritmos controlados por tipo e dados.

Declaração e padrões de tipo

Você usa padrões de declaração e tipo para verificar se o tipo de tempo de execução de uma expressão é compatível com um determinado tipo. Com um padrão de declaração, você também pode declarar uma nova variável local. Quando um padrão de declaração corresponde a uma expressão, essa variável recebe um resultado de expressão convertido, como mostra o exemplo a seguir:

object greeting = "Hello, World!";
if (greeting is string message)
{
    Console.WriteLine(message.ToLower());  // output: hello, world!
}

Um padrão de declaração com tipo T corresponde a uma expressão quando um resultado de expressão não é nulo e qualquer uma das seguintes condições é verdadeira:

  • O tipo de tempo de execução de um resultado de expressão é T.

  • O tipo de tempo de execução de um resultado de expressão deriva do tipo T, implementa a interface Tou outra conversão de referência implícita existe dele para T. O exemplo a seguir demonstra dois casos em que essa condição é verdadeira:

    var numbers = new int[] { 10, 20, 30 };
    Console.WriteLine(GetSourceLabel(numbers));  // output: 1
    
    var letters = new List<char> { 'a', 'b', 'c', 'd' };
    Console.WriteLine(GetSourceLabel(letters));  // output: 2
    
    static int GetSourceLabel<T>(IEnumerable<T> source) => source switch
    {
        Array array => 1,
        ICollection<T> collection => 2,
        _ => 3,
    };
    

    No exemplo anterior, na primeira chamada para o GetSourceLabel método, o primeiro padrão corresponde a um valor de argumento porque o tipo int[] de tempo de execução do argumento deriva do Array tipo. Na segunda chamada para o GetSourceLabel método, o tipo List<T> de tempo de execução do argumento não deriva do Array tipo, mas implementa a ICollection<T> interface.

  • O tipo de tempo de execução de um resultado de expressão é um tipo de valor anulável com o tipo Tsubjacente .

  • Existe uma conversão de boxe ou unboxing do tipo de tempo de execução de um resultado de expressão para o tipo T.

O exemplo a seguir demonstra as duas últimas condições:

int? xNullable = 7;
int y = 23;
object yBoxed = y;
if (xNullable is int a && yBoxed is int b)
{
    Console.WriteLine(a + b);  // output: 30
}

Se quiser verificar apenas o tipo de uma expressão, você pode usar um descarte _ no lugar do nome de uma variável, como mostra o exemplo a seguir:

public abstract class Vehicle {}
public class Car : Vehicle {}
public class Truck : Vehicle {}

public static class TollCalculator
{
    public static decimal CalculateToll(this Vehicle vehicle) => vehicle switch
    {
        Car _ => 2.00m,
        Truck _ => 7.50m,
        null => throw new ArgumentNullException(nameof(vehicle)),
        _ => throw new ArgumentException("Unknown type of a vehicle", nameof(vehicle)),
    };
}

Para esse efeito, você pode usar um padrão de tipo, como mostra o exemplo a seguir:

public static decimal CalculateToll(this Vehicle vehicle) => vehicle switch
{
    Car => 2.00m,
    Truck => 7.50m,
    null => throw new ArgumentNullException(nameof(vehicle)),
    _ => throw new ArgumentException("Unknown type of a vehicle", nameof(vehicle)),
};

Como um padrão de declaração, um padrão de tipo corresponde a uma expressão quando um resultado de expressão não é nulo e seu tipo de tempo de execução satisfaz qualquer uma das condições listadas acima.

Para verificar se não é nulo, você pode usar um padrão constante negado null, como mostra o exemplo a seguir:

if (input is not null)
{
    // ...
}

Para obter mais informações, consulte as seções Padrão de declaração e Padrão de tipo das notas de proposta de recurso.

Padrão constante

Você usa um padrão constante para testar se um resultado de expressão é igual a uma constante especificada, como mostra o exemplo a seguir:

public static decimal GetGroupTicketPrice(int visitorCount) => visitorCount switch
{
    1 => 12.0m,
    2 => 20.0m,
    3 => 27.0m,
    4 => 32.0m,
    0 => 0.0m,
    _ => throw new ArgumentException($"Not supported number of visitors: {visitorCount}", nameof(visitorCount)),
};

Em um padrão constante, você pode usar qualquer expressão constante, como:

A expressão deve ser um tipo conversível para o tipo constante, com uma exceção: uma expressão cujo tipo é Span<char> ou ReadOnlySpan<char> pode ser correspondido com cadeias de caracteres constantes em C# 11 e versões posteriores.

Use um padrão constante para verificar nullo , como mostra o exemplo a seguir:

if (input is null)
{
    return;
}

O compilador garante que nenhum operador == de igualdade sobrecarregado pelo usuário seja invocado quando a expressão x is null é avaliada.

Você pode usar um padrão constante negado null para verificar se não é nulo, como mostra o exemplo a seguir:

if (input is not null)
{
    // ...
}

Para obter mais informações, consulte a seção Padrão constante da nota de proposta de recurso.

Padrões relacionais

Você usa um padrão relacional para comparar um resultado de expressão com uma constante, como mostra o exemplo a seguir:

Console.WriteLine(Classify(13));  // output: Too high
Console.WriteLine(Classify(double.NaN));  // output: Unknown
Console.WriteLine(Classify(2.4));  // output: Acceptable

static string Classify(double measurement) => measurement switch
{
    < -4.0 => "Too low",
    > 10.0 => "Too high",
    double.NaN => "Unknown",
    _ => "Acceptable",
};

Em um padrão relacional, você pode usar qualquer um dos operadores < relacionais , >, <=, ou >=. A parte direita de um padrão relacional deve ser uma expressão constante. A expressão constante pode ser do tipo inteiro, ponto flutuante, char ou enum .

Para verificar se um resultado de expressão está em um determinado intervalo, faça a correspondência com um padrão conjuntivoand, como mostra o exemplo a seguir:

Console.WriteLine(GetCalendarSeason(new DateTime(2021, 3, 14)));  // output: spring
Console.WriteLine(GetCalendarSeason(new DateTime(2021, 7, 19)));  // output: summer
Console.WriteLine(GetCalendarSeason(new DateTime(2021, 2, 17)));  // output: winter

static string GetCalendarSeason(DateTime date) => date.Month switch
{
    >= 3 and < 6 => "spring",
    >= 6 and < 9 => "summer",
    >= 9 and < 12 => "autumn",
    12 or (>= 1 and < 3) => "winter",
    _ => throw new ArgumentOutOfRangeException(nameof(date), $"Date with unexpected month: {date.Month}."),
};

Se um resultado de expressão for null ou não convertido para o tipo de uma constante por uma conversão anulável ou unboxing, um padrão relacional não corresponderá a uma expressão.

Para obter mais informações, consulte a seção Padrões relacionais da nota de proposta de recurso.

Padrões lógicos

Use os notcombinadores , ande or pattern para criar os seguintes padrões lógicos:

  • Padrão de not negação que corresponde a uma expressão quando o padrão negado não corresponde à expressão. O exemplo a seguir mostra como você pode negar um padrão constante null para verificar se uma expressão não é nula:

    if (input is not null)
    {
        // ...
    }
    
  • Padrão conjuntivo and que corresponde a uma expressão quando ambos os padrões correspondem à expressão. O exemplo a seguir mostra como você pode combinar padrões relacionais para verificar se um valor está em um determinado intervalo:

    Console.WriteLine(Classify(13));  // output: High
    Console.WriteLine(Classify(-100));  // output: Too low
    Console.WriteLine(Classify(5.7));  // output: Acceptable
    
    static string Classify(double measurement) => measurement switch
    {
        < -40.0 => "Too low",
        >= -40.0 and < 0 => "Low",
        >= 0 and < 10.0 => "Acceptable",
        >= 10.0 and < 20.0 => "High",
        >= 20.0 => "Too high",
        double.NaN => "Unknown",
    };
    
  • Padrão disjuntivo or que corresponde a uma expressão quando qualquer um dos padrões corresponde à expressão, como mostra o exemplo a seguir:

    Console.WriteLine(GetCalendarSeason(new DateTime(2021, 1, 19)));  // output: winter
    Console.WriteLine(GetCalendarSeason(new DateTime(2021, 10, 9)));  // output: autumn
    Console.WriteLine(GetCalendarSeason(new DateTime(2021, 5, 11)));  // output: spring
    
    static string GetCalendarSeason(DateTime date) => date.Month switch
    {
        3 or 4 or 5 => "spring",
        6 or 7 or 8 => "summer",
        9 or 10 or 11 => "autumn",
        12 or 1 or 2 => "winter",
        _ => throw new ArgumentOutOfRangeException(nameof(date), $"Date with unexpected month: {date.Month}."),
    };
    

Como mostra o exemplo anterior, você pode usar repetidamente os combinadores de padrão em um padrão.

Precedência e ordem de controlo

Os combinadores de padrão são ordenados da maior precedência para a menor da seguinte forma:

  • not
  • and
  • or

Quando um padrão lógico é um padrão de uma is expressão, a precedência dos combinadores de padrões lógicos é maior do que a precedência dos operadores lógicos ( operadores lógicos bitwise e booleanos ). Caso contrário, a precedência de combinadores de padrões lógicos é menor do que a precedência de operadores lógicos e lógicos condicionais. Para obter a lista completa de operadores C# ordenados por nível de precedência, consulte a seção Precedência do operador do artigo Operadores C#.

Para especificar explicitamente a precedência, use parênteses, como mostra o exemplo a seguir:

static bool IsLetter(char c) => c is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z');

Nota

A ordem em que os padrões são verificados é indefinida. Em tempo de execução, os padrões aninhados à direita de e and padrões podem ser verificados or primeiro.

Para obter mais informações, consulte a seção Combinadores de padrões da nota de proposta de recurso.

Padrão de propriedade

Você usa um padrão de propriedade para fazer a correspondência entre as propriedades ou campos de uma expressão e os padrões aninhados, como mostra o exemplo a seguir:

static bool IsConferenceDay(DateTime date) => date is { Year: 2020, Month: 5, Day: 19 or 20 or 21 };

Um padrão de propriedade corresponde a uma expressão quando um resultado de expressão não é nulo e cada padrão aninhado corresponde à propriedade ou campo correspondente do resultado da expressão.

Você também pode adicionar uma verificação de tipo em tempo de execução e uma declaração de variável a um padrão de propriedade, como mostra o exemplo a seguir:

Console.WriteLine(TakeFive("Hello, world!"));  // output: Hello
Console.WriteLine(TakeFive("Hi!"));  // output: Hi!
Console.WriteLine(TakeFive(new[] { '1', '2', '3', '4', '5', '6', '7' }));  // output: 12345
Console.WriteLine(TakeFive(new[] { 'a', 'b', 'c' }));  // output: abc

static string TakeFive(object input) => input switch
{
    string { Length: >= 5 } s => s.Substring(0, 5),
    string s => s,

    ICollection<char> { Count: >= 5 } symbols => new string(symbols.Take(5).ToArray()),
    ICollection<char> symbols => new string(symbols.ToArray()),

    null => throw new ArgumentNullException(nameof(input)),
    _ => throw new ArgumentException("Not supported input type."),
};

Um padrão de propriedade é um padrão recursivo. Ou seja, você pode usar qualquer padrão como um padrão aninhado. Use um padrão de propriedade para fazer a correspondência entre partes de dados e padrões aninhados, como mostra o exemplo a seguir:

public record Point(int X, int Y);
public record Segment(Point Start, Point End);

static bool IsAnyEndOnXAxis(Segment segment) =>
    segment is { Start: { Y: 0 } } or { End: { Y: 0 } };

O exemplo anterior usa o combinador de padrões e os or tipos de registro.

A partir do C# 10, você pode fazer referência a propriedades aninhadas ou campos dentro de um padrão de propriedade. Esse recurso é conhecido como um padrão de propriedade estendida. Por exemplo, você pode refatorar o método do exemplo anterior no seguinte código equivalente:

static bool IsAnyEndOnXAxis(Segment segment) =>
    segment is { Start.Y: 0 } or { End.Y: 0 };

Para obter mais informações, consulte a seção Padrão de propriedade da nota de proposta de recurso e a nota de proposta de recurso Padrões de propriedade estendidos.

Gorjeta

Você pode usar a regra de estilo Simplificar padrão de propriedade (IDE0170) para melhorar a legibilidade do código, sugerindo locais para usar padrões de propriedade estendidos.

Padrão posicional

Você usa um padrão posicional para desconstruir um resultado de expressão e fazer a correspondência entre os valores resultantes e os padrões aninhados correspondentes, como mostra o exemplo a seguir:

public readonly struct Point
{
    public int X { get; }
    public int Y { get; }

    public Point(int x, int y) => (X, Y) = (x, y);

    public void Deconstruct(out int x, out int y) => (x, y) = (X, Y);
}

static string Classify(Point point) => point switch
{
    (0, 0) => "Origin",
    (1, 0) => "positive X basis end",
    (0, 1) => "positive Y basis end",
    _ => "Just a point",
};

No exemplo anterior, o tipo de uma expressão contém o método Deconstruct , que é usado para desconstruir um resultado de expressão. Você também pode combinar expressões de tipos de tupla com padrões posicionais. Dessa forma, você pode combinar várias entradas com vários padrões, como mostra o exemplo a seguir:

static decimal GetGroupTicketPriceDiscount(int groupSize, DateTime visitDate)
    => (groupSize, visitDate.DayOfWeek) switch
    {
        (<= 0, _) => throw new ArgumentException("Group size must be positive."),
        (_, DayOfWeek.Saturday or DayOfWeek.Sunday) => 0.0m,
        (>= 5 and < 10, DayOfWeek.Monday) => 20.0m,
        (>= 10, DayOfWeek.Monday) => 30.0m,
        (>= 5 and < 10, _) => 12.0m,
        (>= 10, _) => 15.0m,
        _ => 0.0m,
    };

O exemplo anterior usa padrões relacionais e lógicos .

Você pode usar os nomes de elementos e Deconstruct parâmetros de tupla em um padrão posicional, como mostra o exemplo a seguir:

var numbers = new List<int> { 1, 2, 3 };
if (SumAndCount(numbers) is (Sum: var sum, Count: > 0))
{
    Console.WriteLine($"Sum of [{string.Join(" ", numbers)}] is {sum}");  // output: Sum of [1 2 3] is 6
}

static (double Sum, int Count) SumAndCount(IEnumerable<int> numbers)
{
    int sum = 0;
    int count = 0;
    foreach (int number in numbers)
    {
        sum += number;
        count++;
    }
    return (sum, count);
}

Você também pode estender um padrão posicional de qualquer uma das seguintes maneiras:

  • Adicione uma verificação de tipo em tempo de execução e uma declaração de variável, como mostra o exemplo a seguir:

    public record Point2D(int X, int Y);
    public record Point3D(int X, int Y, int Z);
    
    static string PrintIfAllCoordinatesArePositive(object point) => point switch
    {
        Point2D (> 0, > 0) p => p.ToString(),
        Point3D (> 0, > 0, > 0) p => p.ToString(),
        _ => string.Empty,
    };
    

    O exemplo anterior usa registros posicionais que implicitamente fornecem o Deconstruct método.

  • Use um padrão de propriedade dentro de um padrão posicional, como mostra o exemplo a seguir:

    public record WeightedPoint(int X, int Y)
    {
        public double Weight { get; set; }
    }
    
    static bool IsInDomain(WeightedPoint point) => point is (>= 0, >= 0) { Weight: >= 0.0 };
    
  • Combine dois usos anteriores, como mostra o exemplo a seguir:

    if (input is WeightedPoint (> 0, > 0) { Weight: > 0.0 } p)
    {
        // ..
    }
    

Um padrão posicional é um padrão recursivo. Ou seja, você pode usar qualquer padrão como um padrão aninhado.

Para obter mais informações, consulte a seção Padrão posicional da nota de proposta de recurso.

var padrão

Use um var padrão para corresponder a qualquer expressão, incluindo null, e atribua seu resultado a uma nova variável local, como mostra o exemplo a seguir:

static bool IsAcceptable(int id, int absLimit) =>
    SimulateDataFetch(id) is var results 
    && results.Min() >= -absLimit 
    && results.Max() <= absLimit;

static int[] SimulateDataFetch(int id)
{
    var rand = new Random();
    return Enumerable
               .Range(start: 0, count: 5)
               .Select(s => rand.Next(minValue: -10, maxValue: 11))
               .ToArray();
}

Um var padrão é útil quando você precisa de uma variável temporária dentro de uma expressão booleana para manter o resultado de cálculos intermediários. Você também pode usar um var padrão quando precisar executar mais verificações em when protetores de caso de uma switch expressão ou instrução, como mostra o exemplo a seguir:

public record Point(int X, int Y);

static Point Transform(Point point) => point switch
{
    var (x, y) when x < y => new Point(-x, y),
    var (x, y) when x > y => new Point(x, -y),
    var (x, y) => new Point(x, y),
};

static void TestTransform()
{
    Console.WriteLine(Transform(new Point(1, 2)));  // output: Point { X = -1, Y = 2 }
    Console.WriteLine(Transform(new Point(5, 2)));  // output: Point { X = 5, Y = -2 }
}

No exemplo anterior, o padrão var (x, y) é equivalente a um padrão (var x, var y)posicional.

Em um var padrão, o tipo de uma variável declarada é o tipo de tempo de compilação da expressão que é correspondida com o padrão.

Para obter mais informações, consulte a seção Padrão Var da nota de proposta de recurso.

Padrão de descarte

Você usa um padrão _ de descarte para corresponder a qualquer expressão, incluindo null, como mostra o exemplo a seguir:

Console.WriteLine(GetDiscountInPercent(DayOfWeek.Friday));  // output: 5.0
Console.WriteLine(GetDiscountInPercent(null));  // output: 0.0
Console.WriteLine(GetDiscountInPercent((DayOfWeek)10));  // output: 0.0

static decimal GetDiscountInPercent(DayOfWeek? dayOfWeek) => dayOfWeek switch
{
    DayOfWeek.Monday => 0.5m,
    DayOfWeek.Tuesday => 12.5m,
    DayOfWeek.Wednesday => 7.5m,
    DayOfWeek.Thursday => 12.5m,
    DayOfWeek.Friday => 5.0m,
    DayOfWeek.Saturday => 2.5m,
    DayOfWeek.Sunday => 2.0m,
    _ => 0.0m,
};

No exemplo anterior, um padrão de descarte é usado para manipular null e qualquer valor inteiro que não tenha o membro correspondente da DayOfWeek enumeração. Isso garante que uma switch expressão no exemplo manipule todos os valores de entrada possíveis. Se você não usar um padrão de descarte em uma switch expressão e nenhum dos padrões da expressão corresponder a uma entrada, o tempo de execução lançará uma exceção. O compilador gera um aviso se uma switch expressão não manipular todos os valores de entrada possíveis.

Um padrão de descarte não pode ser um padrão em uma is expressão ou instrução switch . Nesses casos, para corresponder a qualquer expressão, use um var padrão com um descarte: var _. Um padrão de descarte pode ser um padrão em uma switch expressão.

Para obter mais informações, consulte a seção Descartar padrão da nota de proposta de recurso.

Padrão entre parênteses

Você pode colocar parênteses em torno de qualquer padrão. Normalmente, você faz isso para enfatizar ou alterar a precedência em padrões lógicos, como mostra o exemplo a seguir:

if (input is not (float or double))
{
    return;
}

Padrões de lista

A partir do C# 11, você pode fazer a correspondência entre uma matriz ou uma lista e uma sequência de padrões, como mostra o exemplo a seguir:

int[] numbers = { 1, 2, 3 };

Console.WriteLine(numbers is [1, 2, 3]);  // True
Console.WriteLine(numbers is [1, 2, 4]);  // False
Console.WriteLine(numbers is [1, 2, 3, 4]);  // False
Console.WriteLine(numbers is [0 or 1, <= 2, >= 3]);  // True

Como mostra o exemplo anterior, um padrão de lista é correspondido quando cada padrão aninhado é correspondido pelo elemento correspondente de uma sequência de entrada. Você pode usar qualquer padrão dentro de um padrão de lista. Para corresponder a qualquer elemento, use o padrão de descarte ou, se você também quiser capturar o elemento, o padrão var, como mostra o exemplo a seguir:

List<int> numbers = new() { 1, 2, 3 };

if (numbers is [var first, _, _])
{
    Console.WriteLine($"The first element of a three-item list is {first}.");
}
// Output:
// The first element of a three-item list is 1.

Os exemplos anteriores correspondem a uma sequência de entrada inteira com um padrão de lista. Para corresponder elementos somente no início e/ou no final de uma sequência de entrada, use o padrão ..de fatia , como mostra o exemplo a seguir:

Console.WriteLine(new[] { 1, 2, 3, 4, 5 } is [> 0, > 0, ..]);  // True
Console.WriteLine(new[] { 1, 1 } is [_, _, ..]);  // True
Console.WriteLine(new[] { 0, 1, 2, 3, 4 } is [> 0, > 0, ..]);  // False
Console.WriteLine(new[] { 1 } is [1, 2, ..]);  // False

Console.WriteLine(new[] { 1, 2, 3, 4 } is [.., > 0, > 0]);  // True
Console.WriteLine(new[] { 2, 4 } is [.., > 0, 2, 4]);  // False
Console.WriteLine(new[] { 2, 4 } is [.., 2, 4]);  // True

Console.WriteLine(new[] { 1, 2, 3, 4 } is [>= 0, .., 2 or 4]);  // True
Console.WriteLine(new[] { 1, 0, 0, 1 } is [1, 0, .., 0, 1]);  // True
Console.WriteLine(new[] { 1, 0, 1 } is [1, 0, .., 0, 1]);  // False

Um padrão de fatia corresponde a zero ou mais elementos. Você pode usar no máximo um padrão de fatia em um padrão de lista. O padrão de fatia só pode aparecer em um padrão de lista.

Você também pode aninhar um subpadrão dentro de um padrão de fatia, como mostra o exemplo a seguir:

void MatchMessage(string message)
{
    var result = message is ['a' or 'A', .. var s, 'a' or 'A']
        ? $"Message {message} matches; inner part is {s}."
        : $"Message {message} doesn't match.";
    Console.WriteLine(result);
}

MatchMessage("aBBA");  // output: Message aBBA matches; inner part is BB.
MatchMessage("apron");  // output: Message apron doesn't match.

void Validate(int[] numbers)
{
    var result = numbers is [< 0, .. { Length: 2 or 4 }, > 0] ? "valid" : "not valid";
    Console.WriteLine(result);
}

Validate(new[] { -1, 0, 1 });  // output: not valid
Validate(new[] { -1, 0, 0, 1 });  // output: valid

Para obter mais informações, consulte a Observação de proposta de recurso Listar padrões .

Especificação da linguagem C#

Para obter mais informações, consulte a seção Padrões e correspondência de padrões da especificação da linguagem C#.

Para obter informações sobre os recursos adicionados no C# 8 e posterior, consulte as seguintes notas de proposta de recurso:

Consulte também