Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Descartes são variáveis de espaço reservado intencionalmente não utilizadas no código do aplicativo. Os descartes são equivalentes a variáveis não atribuídas; eles não têm um valor. Um descarte comunica a intenção para o compilador e outras pessoas que leem seu código: você pretendia ignorar o resultado de uma expressão. Talvez você queira ignorar o resultado de uma expressão, um ou mais membros de uma expressão de tupla, um parâmetro out
para um método ou o destino de uma expressão de correspondência de padrões.
Os descartes tornam a intenção do seu código clara. Um descarte indica que nosso código nunca usa a variável. Eles melhoram sua legibilidade e manutenibilidade.
Você indica que uma variável é um descarte atribuindo-lhe o sublinhado (_
) como seu nome. Por exemplo, a chamada de método a seguir retorna uma tupla na qual o primeiro e o segundo valores são descartes.
area
é uma variável declarada anteriormente definida como o terceiro componente retornado por GetCityInformation
:
(_, _, area) = city.GetCityInformation(cityName);
Você pode usar descartes para especificar parâmetros de entrada não utilizados de uma expressão lambda. Para obter mais informações, consulte os parâmetros de entrada de uma seção de expressão lambda do artigo expressões Lambda .
Quando _
é um descarte válido, tentar recuperar seu valor ou usá-lo em uma operação de atribuição gera o erro do compilador CS0103, "O nome '_' não existe no contexto atual". Esse erro ocorre porque _
não é atribuído um valor e pode até não ser atribuído um local de armazenamento. Se fosse uma variável real, você não poderia descartar mais de um valor, como o exemplo anterior.
Desconstrução de objeto e de tupla
Descartes são úteis para trabalhar com tuplas quando seu código de aplicativo usa alguns elementos da tupla, mas ignora outros. Por exemplo, o método a seguir QueryCityDataForYears
retorna uma tupla com o nome de uma cidade, sua área, um ano, a população da cidade para esse ano, um segundo ano e a população da cidade para esse segundo ano. O exemplo mostra a alteração na população entre esses dois anos. Entre os dados disponíveis da tupla, não estamos preocupados com a área da cidade e sabemos o nome da cidade e as duas datas em tempo de design. Como resultado, estamos interessados apenas nos dois valores de população armazenados na tupla e podemos lidar com seus valores restantes como descartes.
var (_, _, _, pop1, _, pop2) = QueryCityDataForYears("New York City", 1960, 2010);
Console.WriteLine($"Population change, 1960 to 2010: {pop2 - pop1:N0}");
static (string, double, int, int, int, int) QueryCityDataForYears(string name, int year1, int year2)
{
int population1 = 0, population2 = 0;
double area = 0;
if (name == "New York City")
{
area = 468.48;
if (year1 == 1960)
{
population1 = 7781984;
}
if (year2 == 2010)
{
population2 = 8175133;
}
return (name, area, year1, population1, year2, population2);
}
return ("", 0, 0, 0, 0, 0);
}
// The example displays the following output:
// Population change, 1960 to 2010: 393,149
Para obter mais informações sobre desconstruir tuplas com descartes, consulte Desconstruindo tuplas e outros tipos.
O Deconstruct
método de uma classe, estrutura ou interface também permite recuperar e desconstruir um conjunto específico de dados de um objeto. Você poderá usar descartes quando estiver interessado em trabalhar com apenas um subconjunto dos valores desconstruídos. O exemplo a seguir desconstrói um Person
objeto em quatro cadeias de caracteres (o primeiro e os sobrenomes, a cidade e o estado), mas descarta o sobrenome e o estado.
using System;
namespace Discards
{
public class Person
{
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string City { get; set; }
public string State { get; set; }
public Person(string fname, string mname, string lname,
string cityName, string stateName)
{
FirstName = fname;
MiddleName = mname;
LastName = lname;
City = cityName;
State = stateName;
}
// Return the first and last name.
public void Deconstruct(out string fname, out string lname)
{
fname = FirstName;
lname = LastName;
}
public void Deconstruct(out string fname, out string mname, out string lname)
{
fname = FirstName;
mname = MiddleName;
lname = LastName;
}
public void Deconstruct(out string fname, out string lname,
out string city, out string state)
{
fname = FirstName;
lname = LastName;
city = City;
state = State;
}
}
class Example
{
public static void Main()
{
var p = new Person("John", "Quincy", "Adams", "Boston", "MA");
// Deconstruct the person object.
var (fName, _, city, _) = p;
Console.WriteLine($"Hello {fName} of {city}!");
// The example displays the following output:
// Hello John of Boston!
}
}
}
Para obter mais informações sobre desconstruir tipos definidos pelo usuário com descartes, consulte Desconstruindo tuplas e outros tipos.
Correspondência de padrões com switch
O padrão de descarte pode ser usado na correspondência de padrões com a expressão switch. Cada expressão, inclusive null
, sempre corresponde ao padrão de descarte.
O exemplo a seguir define um ProvidesFormatInfo
método que usa uma switch
expressão para determinar se um objeto fornece uma IFormatProvider implementação e testa se o objeto é null
. Ele também usa o padrão de descarte para lidar com objetos não nulos de qualquer outro tipo.
object?[] objects = [CultureInfo.CurrentCulture,
CultureInfo.CurrentCulture.DateTimeFormat,
CultureInfo.CurrentCulture.NumberFormat,
new ArgumentException(), null];
foreach (var obj in objects)
ProvidesFormatInfo(obj);
static void ProvidesFormatInfo(object? obj) =>
Console.WriteLine(obj switch
{
IFormatProvider fmt => $"{fmt.GetType()} object",
null => "A null object reference: Its use could result in a NullReferenceException",
_ => "Some object type without format information"
});
// The example displays the following output:
// System.Globalization.CultureInfo object
// System.Globalization.DateTimeFormatInfo object
// System.Globalization.NumberFormatInfo object
// Some object type without format information
// A null object reference: Its use could result in a NullReferenceException
Chamadas para métodos com parâmetros out
Ao chamar o Deconstruct
método para desconstruir um tipo definido pelo usuário (uma instância de uma classe, estrutura ou interface), você pode descartar os valores de argumentos individuais out
. Mas você também pode descartar o valor dos out
argumentos ao chamar qualquer método com um out
parâmetro.
O exemplo a seguir chama o método DateTime.TryParse(String, out DateTime) para determinar se a representação de cadeia de caracteres de uma data é válida na cultura atual. Como o exemplo se preocupa apenas com a validação da cadeia de caracteres de data e não com a análise dela para extrair a data, o out
argumento para o método é um descarte.
string[] dateStrings = ["05/01/2018 14:57:32.8", "2018-05-01 14:57:32.8",
"2018-05-01T14:57:32.8375298-04:00", "5/01/2018",
"5/01/2018 14:57:32.80 -07:00",
"1 May 2018 2:57:32.8 PM", "16-05-2018 1:00:32 PM",
"Fri, 15 May 2018 20:10:57 GMT"];
foreach (string dateString in dateStrings)
{
if (DateTime.TryParse(dateString, out _))
Console.WriteLine($"'{dateString}': valid");
else
Console.WriteLine($"'{dateString}': invalid");
}
// The example displays output like the following:
// '05/01/2018 14:57:32.8': valid
// '2018-05-01 14:57:32.8': valid
// '2018-05-01T14:57:32.8375298-04:00': valid
// '5/01/2018': valid
// '5/01/2018 14:57:32.80 -07:00': valid
// '1 May 2018 2:57:32.8 PM': valid
// '16-05-2018 1:00:32 PM': invalid
// 'Fri, 15 May 2018 20:10:57 GMT': invalid
Um descarte autônomo
Você pode usar um descarte autônomo para indicar qualquer variável que você optar por ignorar. Um uso típico é usar uma atribuição para garantir que um argumento não seja nulo. O código a seguir usa um descarte para forçar uma atribuição. O lado direito da atribuição usa o operador de avaliação de nulo para lançar um System.ArgumentNullException quando o argumento é null
. O código não precisa do resultado da atribuição, portanto, ele é descartado. A expressão força uma verificação de nulo. O descarte esclarece sua intenção: o resultado da atribuição não é necessário ou usado.
public static void Method(string arg)
{
_ = arg ?? throw new ArgumentNullException(paramName: nameof(arg), message: "arg can't be null");
// Do work with arg.
}
O exemplo a seguir usa um descarte autônomo para ignorar o Task objeto retornado por uma operação assíncrona. Atribuir a tarefa tem o efeito de suprimir a exceção que a operação gera quando está prestes a ser concluída. Isso deixa sua intenção clara: você deseja descartar o Task
, e ignorar todos os erros gerados a partir dessa operação assíncrona.
private static async Task ExecuteAsyncMethods()
{
Console.WriteLine("About to launch a task...");
_ = Task.Run(() =>
{
var iterations = 0;
for (int ctr = 0; ctr < int.MaxValue; ctr++)
iterations++;
Console.WriteLine("Completed looping operation...");
throw new InvalidOperationException();
});
await Task.Delay(5000);
Console.WriteLine("Exiting after 5 second delay");
}
// The example displays output like the following:
// About to launch a task...
// Completed looping operation...
// Exiting after 5 second delay
Sem atribuir a tarefa a um descarte, o código a seguir gera um aviso do compilador:
private static async Task ExecuteAsyncMethods()
{
Console.WriteLine("About to launch a task...");
// CS4014: Because this call is not awaited, execution of the current method continues before the call is completed.
// Consider applying the 'await' operator to the result of the call.
Task.Run(() =>
{
var iterations = 0;
for (int ctr = 0; ctr < int.MaxValue; ctr++)
iterations++;
Console.WriteLine("Completed looping operation...");
throw new InvalidOperationException();
});
await Task.Delay(5000);
Console.WriteLine("Exiting after 5 second delay");
Observação
Se você executar um dos dois exemplos anteriores usando um depurador, o depurador interromperá o programa quando a exceção for lançada. Sem um depurador anexado, a exceção é silenciosamente ignorada em ambos os casos.
_
também é um identificador válido. Quando usado fora de um contexto com suporte, _
é tratado não como um descarte, mas como uma variável válida. Se um identificador nomeado _
já estiver no escopo, o uso de _
como um descarte autônomo poderá resultar em:
- Modificação acidental do valor da variável no escopo
_
atribuindo-lhe o valor do descarte pretendido. Por exemplo:private static void ShowValue(int _) { byte[] arr = [0, 0, 1, 2]; _ = BitConverter.ToInt32(arr, 0); Console.WriteLine(_); } // The example displays the following output: // 33619968
- Um erro do compilador por violar a segurança de tipos. Por exemplo:
private static bool RoundTrips(int _) { string value = _.ToString(); int newValue = 0; _ = Int32.TryParse(value, out newValue); return _ == newValue; } // The example displays the following compiler error: // error CS0029: Cannot implicitly convert type 'bool' to 'int'