Métodos em C#
Um método é um bloco de código que contém uma série de instruções. Um programa faz com que as instruções sejam executadas chamando o método e especificando os argumentos de método necessários. No C#, todas as instruções executadas são realizadas no contexto de um método.
Observação
Este tópico aborda os métodos nomeados. Para obter mais informações sobre funções anônimas, consulte Expressões lambda.
Assinaturas de método
Os métodos são declarados em class
, record
ou struct
especificando:
- Um nível de acesso opcional, como
public
ouprivate
. O padrão éprivate
. - Modificadores opcionais como
abstract
ousealed
. - O valor retornado ou
void
se o método não tiver nenhum. - O nome do método.
- Quaisquer parâmetros de método. Os parâmetros de método estão entre parênteses e separados por vírgulas. Parênteses vazios indicam que o método não requer parâmetros.
Essas partes juntas formam a assinatura do método.
Importante
Um tipo de retorno de um método não faz parte da assinatura do método para fins de sobrecarga de método. No entanto, ele faz parte da assinatura do método ao determinar a compatibilidade entre um delegado e o método para o qual ele aponta.
O exemplo a seguir define uma classe chamada Motorcycle
que contém cinco métodos:
namespace MotorCycleExample
{
abstract class Motorcycle
{
// Anyone can call this.
public void StartEngine() {/* Method statements here */ }
// Only derived classes can call this.
protected void AddGas(int gallons) { /* Method statements here */ }
// Derived classes can override the base class implementation.
public virtual int Drive(int miles, int speed) { /* Method statements here */ return 1; }
// Derived classes can override the base class implementation.
public virtual int Drive(TimeSpan time, int speed) { /* Method statements here */ return 0; }
// Derived classes must implement this.
public abstract double GetTopSpeed();
}
A classe Motorcycle
inclui um método sobrecarregado, Drive
. Dois métodos têm o mesmo nome, mas são diferenciados por seus tipos de parâmetro.
Invocação de método
Os métodos podem ser de instância ou estáticos. Você deve criar uma instância de um objeto para invocar um método de instância nessa instância. Um método de instância opera nessa instância e seus dados. Você invoca um método estático referenciando o nome do tipo ao qual o método pertence; os métodos estáticos não operam nos dados da instância. Tentar chamar um método estático por meio de uma instância do objeto gera um erro do compilador.
Chamar um método é como acessar um campo. Após o nome do objeto (se você estiver chamando um método de instância) ou o nome do tipo (se você estiver chamando um método static
), adicione um ponto, o nome do método e parênteses. Os argumentos são listados dentro dos parênteses e são separados por vírgulas.
A definição do método especifica os nomes e tipos de quaisquer parâmetros obrigatórios. Quando um chamador invoca o método, ele fornece valores concretos, chamados argumentos, para cada parâmetro. Os argumentos devem ser compatíveis com o tipo de parâmetro, mas o nome do argumento, se for usado no código de chamada, não precisa ser o mesmo do parâmetro nomeado definido no método. No exemplo a seguir, o método Square
inclui um único parâmetro do tipo int
chamado i. A primeira chamada do método passa para o método Square
uma variável do tipo int
chamada num, a segunda, uma constante numérica e a terceira, uma expressão.
public static class SquareExample
{
public static void Main()
{
// Call with an int variable.
int num = 4;
int productA = Square(num);
// Call with an integer literal.
int productB = Square(12);
// Call with an expression that evaluates to int.
int productC = Square(productA * 3);
}
static int Square(int i)
{
// Store input argument in a local variable.
int input = i;
return input * input;
}
}
A forma mais comum de invocação de método usa argumentos posicionais, ela fornece os argumentos na mesma ordem que os parâmetros de método. Os métodos da classe Motorcycle
, podem, portanto, ser chamados como no exemplo a seguir. A chamada para o método Drive
, por exemplo, inclui dois argumentos que correspondem aos dois parâmetros na sintaxe do método. O primeiro se torna o valor do parâmetro miles
. O segundo se torna o valor do parâmetro speed
.
class TestMotorcycle : Motorcycle
{
public override double GetTopSpeed() => 108.4;
static void Main()
{
var moto = new TestMotorcycle();
moto.StartEngine();
moto.AddGas(15);
_ = moto.Drive(5, 20);
double speed = moto.GetTopSpeed();
Console.WriteLine("My top speed is {0}", speed);
}
}
Você também pode usar argumentos nomeados em vez de argumentos posicionais ao invocar um método. Ao usar argumentos nomeados, você especifica o nome do parâmetro seguido por dois pontos (":") e o argumento. Os argumentos do método podem aparecer em qualquer ordem, desde que todos os argumentos necessários estejam presentes. O exemplo a seguir usa argumentos nomeados para invocar o método TestMotorcycle.Drive
. Neste exemplo, os argumentos nomeados são passados na ordem oposta da lista de parâmetros do método.
namespace NamedMotorCycle;
class TestMotorcycle : Motorcycle
{
public override int Drive(int miles, int speed) =>
(int)Math.Round((double)miles / speed, 0);
public override double GetTopSpeed() => 108.4;
static void Main()
{
var moto = new TestMotorcycle();
moto.StartEngine();
moto.AddGas(15);
int travelTime = moto.Drive(miles: 170, speed: 60);
Console.WriteLine("Travel time: approx. {0} hours", travelTime);
}
}
// The example displays the following output:
// Travel time: approx. 3 hours
Você pode invocar um método usando argumentos posicionais e argumentos nomeados. No entanto, os argumentos posicionais só podem seguir argumentos nomeados quando os argumentos nomeados estiverem nas posições corretas. O exemplo a seguir invoca o método TestMotorcycle.Drive
do exemplo anterior usando um argumento posicional e um argumento nomeado.
int travelTime = moto.Drive(170, speed: 55);
Métodos herdados e substituídos
Além dos membros que são definidos explicitamente em um tipo, um tipo herda membros definidos em suas classes base. Como todos os tipos no sistema de tipos gerenciado são herdados direta ou indiretamente da classe Object, todos os tipos herdam seus membros, como Equals(Object), GetType() e ToString(). O exemplo a seguir define uma classe Person
, instancia dois objetos Person
e chama o método Person.Equals
para determinar se os dois objetos são iguais. O método Equals
, no entanto, não é definido na classe Person
; ele é herdado do Object.
public class Person
{
public string FirstName = default!;
}
public static class ClassTypeExample
{
public static void Main()
{
Person p1 = new() { FirstName = "John" };
Person p2 = new() { FirstName = "John" };
Console.WriteLine("p1 = p2: {0}", p1.Equals(p2));
}
}
// The example displays the following output:
// p1 = p2: False
Tipos podem substituir membros herdados usando a palavra-chave override
e fornecendo uma implementação para o método substituído. A assinatura do método precisa ser igual à do método substituído. O exemplo a seguir é semelhante ao anterior, exceto que ele substitui o método Equals(Object). (Ele também substitui o método GetHashCode(), uma vez que os dois métodos destinam-se a fornecer resultados consistentes.)
namespace methods;
public class Person
{
public string FirstName = default!;
public override bool Equals(object? obj) =>
obj is Person p2 &&
FirstName.Equals(p2.FirstName);
public override int GetHashCode() => FirstName.GetHashCode();
}
public static class Example
{
public static void Main()
{
Person p1 = new() { FirstName = "John" };
Person p2 = new() { FirstName = "John" };
Console.WriteLine("p1 = p2: {0}", p1.Equals(p2));
}
}
// The example displays the following output:
// p1 = p2: True
Passando parâmetros
Os tipos no C# são tipos de valor ou tipos de referência. Para obter uma lista de tipos de valor internos, consulte Tipos. Por padrão, os tipos de referência e tipos de valor são passados por valor para um método.
Passando parâmetros por valor
Quando um tipo de valor é passado para um método por valor, uma cópia do objeto, em vez do próprio objeto, é passada para o método. Portanto, as alterações no objeto do método chamado não têm efeito no objeto original quando o controle retorna ao chamador.
O exemplo a seguir passa um tipo de valor para um método por valor e o método chamado tenta alterar o valor do tipo de valor. Ele define uma variável do tipo int
, que é um tipo de valor, inicializa o valor para 20 e o passa para um método chamado ModifyValue
que altera o valor da variável para 30. No entanto, quando o método retorna, o valor da variável permanece inalterado.
public static class ByValueExample
{
public static void Main()
{
var value = 20;
Console.WriteLine("In Main, value = {0}", value);
ModifyValue(value);
Console.WriteLine("Back in Main, value = {0}", value);
}
static void ModifyValue(int i)
{
i = 30;
Console.WriteLine("In ModifyValue, parameter value = {0}", i);
return;
}
}
// The example displays the following output:
// In Main, value = 20
// In ModifyValue, parameter value = 30
// Back in Main, value = 20
Quando um objeto do tipo de referência é passado para um método por valor, uma referência ao objeto é passada por valor. Ou seja, o método recebe não o objeto em si, mas um argumento que indica o local do objeto. Se você alterar um membro do objeto usando essa referência, a alteração será refletida no objeto quando o controle retornar para o método de chamada. No entanto, substituir o objeto passado para o método não tem efeito no objeto original quando o controle retorna para o chamador.
O exemplo a seguir define uma classe (que é um tipo de referência) chamada SampleRefType
. Ele cria uma instância de um objeto SampleRefType
, atribui 44 ao seu campo value
e passa o objeto para o método ModifyObject
. Este exemplo faz essencialmente a mesma coisa que o exemplo anterior: ele passa um argumento por valor para um método. Mas como um tipo de referência é usado, o resultado é diferente. A modificação feita em ModifyObject
para o campo obj.value
também muda o campo value
do argumento, rt
, no método Main
para 33, como a saída do exemplo mostra.
public class SampleRefType
{
public int value;
}
public static class ByRefTypeExample
{
public static void Main()
{
var rt = new SampleRefType { value = 44 };
ModifyObject(rt);
Console.WriteLine(rt.value);
}
static void ModifyObject(SampleRefType obj) => obj.value = 33;
}
Passando parâmetros por referência
Você passa um parâmetro por referência quando deseja alterar o valor de um argumento em um método e deseja refletir essa alteração quando o controle retorna para o método de chamada. Para passar um parâmetro por referência, use a palavra-chave ref
ou out
. Você também pode passar um valor por referência para evitar a cópia e ainda evitar modificações usando a palavra-chave in
.
O exemplo a seguir é idêntico ao anterior, exceto que o valor é passado por referência para o método ModifyValue
. Quando o valor do parâmetro é modificado no método ModifyValue
, a alteração no valor é refletida quando o controle retorna ao chamador.
public static class ByRefExample
{
public static void Main()
{
var value = 20;
Console.WriteLine("In Main, value = {0}", value);
ModifyValue(ref value);
Console.WriteLine("Back in Main, value = {0}", value);
}
private static void ModifyValue(ref int i)
{
i = 30;
Console.WriteLine("In ModifyValue, parameter value = {0}", i);
return;
}
}
// The example displays the following output:
// In Main, value = 20
// In ModifyValue, parameter value = 30
// Back in Main, value = 30
Um padrão comum que usa parâmetros pela referência envolve a troca os valores das variáveis. Você passa duas variáveis para um método por referência e o método troca seus conteúdos. O exemplo a seguir troca valores inteiros.
public static class RefSwapExample
{
static void Main()
{
int i = 2, j = 3;
Console.WriteLine("i = {0} j = {1}", i, j);
Swap(ref i, ref j);
Console.WriteLine("i = {0} j = {1}", i, j);
}
static void Swap(ref int x, ref int y) =>
(y, x) = (x, y);
}
// The example displays the following output:
// i = 2 j = 3
// i = 3 j = 2
Passar um parâmetro de tipo de referência permite que você altere o valor da própria referência, em vez de o valor de seus campos ou elementos individuais.
Coleções de parâmetros
Às vezes, o requisito de que você especifique o número exato de argumentos para o método é restritivo. Usando a palavra-chave params
para indicar que um parâmetro é uma coleção de parâmetros, você permite que o método seja chamado com um número variável de argumentos. O parâmetro marcado com a palavra-chave params
deve ser um tipo de coleção e ele deve ser o último parâmetro na lista de parâmetros do método.
Um chamador pode, então, invocar o método de uma das quatro maneiras para o parâmetro params
:
- Passando uma coleção do tipo apropriado que contém o número de elementos desejado. O exemplo usa uma expressão de coleção para que o compilador crie um tipo de coleção apropriado.
- Passando uma lista separada por vírgulas de argumentos individuais do tipo apropriado para o método. O compilador cria o tipo de coleção apropriado.
- Passando
null
. - Não fornecendo um argumento para a coleção de parâmetros.
O exemplo a seguir define um método chamado GetVowels
que retorna todas as vogais de uma coleção de parâmetros. O método Main
ilustra todas as quatro maneiras de invocar o método. Os chamadores não precisam fornecer argumentos para parâmetros que incluem o modificador params
. Nesse caso, o parâmetro é uma coleção vazia.
static class ParamsExample
{
static void Main()
{
string fromArray = GetVowels(["apple", "banana", "pear"]);
Console.WriteLine($"Vowels from collection expression: '{fromArray}'");
string fromMultipleArguments = GetVowels("apple", "banana", "pear");
Console.WriteLine($"Vowels from multiple arguments: '{fromMultipleArguments}'");
string fromNull = GetVowels(null);
Console.WriteLine($"Vowels from null: '{fromNull}'");
string fromNoValue = GetVowels();
Console.WriteLine($"Vowels from no value: '{fromNoValue}'");
}
static string GetVowels(params IEnumerable<string>? input)
{
if (input == null || !input.Any())
{
return string.Empty;
}
char[] vowels = ['A', 'E', 'I', 'O', 'U'];
return string.Concat(
input.SelectMany(
word => word.Where(letter => vowels.Contains(char.ToUpper(letter)))));
}
}
// The example displays the following output:
// Vowels from array: 'aeaaaea'
// Vowels from multiple arguments: 'aeaaaea'
// Vowels from null: ''
// Vowels from no value: ''
Antes do C# 13, o modificador params
só pode ser usado com uma única matriz dimensional.
Parâmetros e argumentos opcionais
Uma definição de método pode especificar que os parâmetros são obrigatórios ou que são opcionais. Por padrão, os parâmetros são obrigatórios. Os parâmetros opcionais são especificados incluindo o valor padrão do parâmetro na definição do método. Quando o método for chamado, se nenhum argumento for fornecido para um parâmetro opcional, o valor padrão será usado em vez disso.
Você atribuirá o valor padrão do parâmetro com um dos tipos de expressões a seguir:
Uma constante, como um número ou uma cadeia de caracteres literal.
Uma expressão do formulário
default(SomeType)
, em queSomeType
pode ser um tipo de valor ou um tipo de referência. Se for um tipo de referência, ele será efetivamente o mesmo que especificarnull
. Você pode usar o literaldefault
, pois o compilador pode inferir o tipo a partir da declaração do parâmetro.Uma expressão da forma
new ValType()
, em queValType
é um tipo de valor. Essa expressão invoca o construtor sem parâmetros implícito do tipo de valor, que não é de fato um membro do tipo.Observação
No C# 10 e posterior, quando uma expressão do formulário
new ValType()
invoca o construtor sem parâmetros definido explicitamente de um tipo de valor, o compilador gera um erro, pois o valor do parâmetro padrão deve ser uma constante de tempo de compilação. Use a expressãodefault(ValType)
ou o literaldefault
para fornecer o valor padrão do parâmetro. Para obter mais informações sobre construtores sem parâmetros, consulte a seção Inicialização de struct e valores padrão do artigo Tipos de estrutura.
Se um método inclui parâmetros obrigatórios e opcionais, os parâmetros opcionais são definidos no final da lista de parâmetros, após todos os parâmetros obrigatórios.
O exemplo a seguir define um método, ExampleMethod
, que tem um parâmetro obrigatório e dois opcionais.
public class Options
{
public void ExampleMethod(int required, int optionalInt = default,
string? description = default)
{
var msg = $"{description ?? "N/A"}: {required} + {optionalInt} = {required + optionalInt}";
Console.WriteLine(msg);
}
}
O chamador deve fornecer um argumento para todos os parâmetros opcionais até o último parâmetro opcional para o qual um argumento é fornecido. No método ExampleMethod
, por exemplo, se o chamador fornecer um argumento para o parâmetro description
, ele deverá fornecer também um para o parâmetro optionalInt
. opt.ExampleMethod(2, 2, "Addition of 2 and 2");
é uma chamada de método válida, opt.ExampleMethod(2, , "Addition of 2 and 0");
gera um erro do compilador de “Argumento ausente”.
Se um método for chamado usando argumentos nomeados ou uma combinação de argumentos posicionais e nomeados, o chamador poderá omitir todos os argumentos após o último argumento posicional na chamada do método.
A exemplo a seguir chama o método ExampleMethod
três vezes. As duas primeiras chamadas de método usam argumentos posicionais. O primeiro omite ambos os argumentos opcionais, enquanto o segundo omite o último argumento. A terceira chamada de método fornece um argumento posicional para o parâmetro obrigatório, mas usa um argumento nomeado para fornecer um valor para o parâmetro description
enquanto omite o argumento optionalInt
.
public static class OptionsExample
{
public static void Main()
{
var opt = new Options();
opt.ExampleMethod(10);
opt.ExampleMethod(10, 2);
opt.ExampleMethod(12, description: "Addition with zero:");
}
}
// The example displays the following output:
// N/A: 10 + 0 = 10
// N/A: 10 + 2 = 12
// Addition with zero:: 12 + 0 = 12
O uso de parâmetros opcionais afeta a resolução de sobrecarga ou a maneira como o compilador do C# determina qual sobrecarga deve ser invocada para uma chamada de método da seguinte maneira:
- Um método, indexador ou construtor é um candidato para a execução se cada um dos parâmetros corresponde, por nome ou posição, a um único argumento e esse argumento pode ser convertido para o tipo do parâmetro.
- Se mais de um candidato for encontrado, as regras de resolução de sobrecarga de conversões preferenciais serão aplicadas aos argumentos que são especificados explicitamente. Os argumentos omitidos para parâmetros opcionais são ignorados.
- Se dois candidatos são considerados igualmente bons, a preferência vai para um candidato que não tenha parâmetros opcionais para os quais argumentos foram omitidos na chamada.
Valores retornados
Os métodos podem retornar um valor para o chamador. Se o tipo de retorno (o tipo listado antes do nome do método) não for void
, o método poderá retornar o valor usando a palavra-chave return
. Uma instrução com a palavra-chave return
seguida por uma variável, constante ou expressão que corresponde ao tipo de retorno retorna esse valor para o chamador do método. Métodos com um tipo de retorno não nulo devem usar a palavra-chave return
para retornar um valor. A palavra-chave return
também interrompe a execução do método.
Se o tipo de retorno for void
, uma instrução return
sem um valor ainda será útil para interromper a execução do método. Sem a palavra-chave return
, a execução do método é interrompida quando chega ao final do bloco de código.
Por exemplo, esses dois métodos usam a palavra-chave return
para retornar inteiros:
class SimpleMath
{
public int AddTwoNumbers(int number1, int number2) =>
number1 + number2;
public int SquareANumber(int number) =>
number * number;
}
Os exemplos acima são membros aptos para expressão. Os membros aptos para expressão retornam o valor retornado pela expressão.
Você também pode optar por definir seus métodos com um corpo de instrução e uma instrução return
:
class SimpleMathExtnsion
{
public int DivideTwoNumbers(int number1, int number2)
{
return number1 / number2;
}
}
Para usar um valor retornado de um método, o método de chamada pode usar a chamada de método em si em qualquer lugar que um valor do mesmo tipo seria suficiente. Você também pode atribuir o valor retornado a uma variável. Por exemplo, os três seguintes exemplos de código alcançam a mesma meta:
int result = obj.AddTwoNumbers(1, 2);
result = obj.SquareANumber(result);
// The result is 9.
Console.WriteLine(result);
result = obj.SquareANumber(obj.AddTwoNumbers(1, 2));
// The result is 9.
Console.WriteLine(result);
result = obj2.DivideTwoNumbers(6,2);
// The result is 3.
Console.WriteLine(result);
Às vezes, você deseja que seu método retorne mais de um único valor. Você usa tipos de tupla e literais de tupla para retornar vários valores. O tipo de tupla define os tipos de dados dos elementos da tupla. Os literais de tupla fornecem os valores reais da tupla retornada. No exemplo a seguir, (string, string, string, int)
define o tipo de tupla retornado pelo método GetPersonalInfo
. A expressão (per.FirstName, per.MiddleName, per.LastName, per.Age)
é a tupla literal, o método retorna o nome, o nome do meio e o sobrenome, juntamente com a idade, de um objeto PersonInfo
.
public (string, string, string, int) GetPersonalInfo(string id)
{
PersonInfo per = PersonInfo.RetrieveInfoById(id);
return (per.FirstName, per.MiddleName, per.LastName, per.Age);
}
O chamador pode consumir a tupla retornada usando o seguinte código:
var person = GetPersonalInfo("111111111");
Console.WriteLine($"{person.Item1} {person.Item3}: age = {person.Item4}");
Os nomes também podem ser atribuídos aos elementos da tupla na definição de tipo de tupla. O exemplo a seguir mostra uma versão alternativa do método GetPersonalInfo
que usa elementos nomeados:
public (string FName, string MName, string LName, int Age) GetPersonalInfo(string id)
{
PersonInfo per = PersonInfo.RetrieveInfoById(id);
return (per.FirstName, per.MiddleName, per.LastName, per.Age);
}
A chamada anterior para o método GetPersonalInfo
pode ser modificada da seguinte maneira:
var person = GetPersonalInfo("111111111");
Console.WriteLine($"{person.FName} {person.LName}: age = {person.Age}");
Se um método usar uma matriz como parâmetro e modificar o valor de elementos individuais, não será necessário que o método retorne a matriz. O C# passa todos os tipos de referência por valor e o valor de uma referência de matriz é o ponteiro para a matriz. No exemplo a seguir, as alterações no conteúdo da matriz values
realizados pelo método DoubleValues
são observáveis por qualquer código que faz referência à matriz.
public static class ArrayValueExample
{
static void Main()
{
int[] values = [2, 4, 6, 8];
DoubleValues(values);
foreach (var value in values)
{
Console.Write("{0} ", value);
}
}
public static void DoubleValues(int[] arr)
{
for (var ctr = 0; ctr <= arr.GetUpperBound(0); ctr++)
{
arr[ctr] *= 2;
}
}
}
// The example displays the following output:
// 4 8 12 16
Métodos de extensão
Normalmente, há duas maneiras de adicionar um método a um tipo existente:
- Modificar o código-fonte para esse tipo. Modificar a fonte criar uma alteração interruptiva, se você também adicionar campos de dados privados para dar suporte ao método.
- Definir o novo método em uma classe derivada. Não é possível adicionar um método dessa forma usando a herança para outros tipos, como estruturas e enumerações. Isso também não pode ser usado para “adicionar” um método a uma classe selada.
Os métodos de extensão permitem que você “adicione” um método a um tipo existente sem modificar o tipo em si ou implementar o novo método em um tipo herdado. O método de extensão também não precisa residir no mesmo assembly do tipo que ele estende. Você chama um método de extensão como se fosse um membro definido de um tipo.
Para obter mais informações, consulte Métodos de extensão.
Métodos assíncronos
Usando o recurso async, você pode invocar métodos assíncronos sem usar retornos de chamada explícitos ou dividir manualmente seu código entre vários métodos ou expressões lambda.
Se marcar um método com o modificador async, você poderá usar o operador await no método. Quando o controle atingir uma expressão await
no método assíncrono, o controle retornará para o chamador se a tarefa aguardada não estiver concluída, e o progresso no método com a palavra-chave await
será suspenso até a tarefa aguardada ser concluída. Quando a tarefa for concluída, a execução poderá ser retomada no método.
Observação
Um método assíncrono retorna para o chamador quando encontra o primeiro objeto esperado que ainda não está completo ou chega ao final do método assíncrono, o que ocorrer primeiro.
Um método assíncrono normalmente tem um tipo de retorno Task<TResult>, Task, IAsyncEnumerable<T> ou void
. O tipo de retorno void
é usado principalmente para definir manipuladores de eventos, nos quais o tipo de retorno void
é necessário. Um método assíncrono que retorna void
não pode ser aguardado e o chamador de um método de retorno nulo não pode capturar as exceções que esse método gera. Um método assíncrono pode ter qualquer tipo de retorno como os de tarefa.
No exemplo a seguir, DelayAsync
é um método assíncrono que contém uma instrução return que retorna um inteiro. Como é um método assíncrono, sua declaração de método deve ter um tipo de retorno Task<int>
. Como o tipo de retorno é Task<int>
, a avaliação da expressão await
em DoSomethingAsync
produz um inteiro, como a instrução int result = await delayTask
a seguir demonstra.
class Program
{
static Task Main() => DoSomethingAsync();
static async Task DoSomethingAsync()
{
Task<int> delayTask = DelayAsync();
int result = await delayTask;
// The previous two statements may be combined into
// the following statement.
//int result = await DelayAsync();
Console.WriteLine($"Result: {result}");
}
static async Task<int> DelayAsync()
{
await Task.Delay(100);
return 5;
}
}
// Example output:
// Result: 5
Um método assíncrono não pode declarar os parâmetros in, ref nem out, mas pode chamar métodos que tenham esses parâmetros.
Para obter mais informações sobre os métodos assíncronos, consulte Programação assíncrona com async e await e Tipos de retorno Async.
Membros aptos para expressão
É comum ter definições de método que retornam imediatamente com o resultado de uma expressão ou que têm uma única instrução como o corpo do método. Há um atalho de sintaxe para definir esses métodos usando =>
:
public Point Move(int dx, int dy) => new Point(x + dx, y + dy);
public void Print() => Console.WriteLine(First + " " + Last);
// Works with operators, properties, and indexers too.
public static Complex operator +(Complex a, Complex b) => a.Add(b);
public string Name => First + " " + Last;
public Customer this[long id] => store.LookupCustomer(id);
Se o método retornar void
ou for um método assíncrono, o corpo do método deverá ser uma expressão de instrução (igual aos lambdas). Para propriedades e indexadores, eles devem ser somente leitura e você não usa a palavra-chave do acessador get
.
Iterators
Um iterador realiza uma iteração personalizada em uma coleção, como uma lista ou uma matriz. Um iterador usa a instrução yield return para retornar um elemento de cada vez. Quando uma instrução yield return
for atingida, o local atual será lembrado para que o chamador possa solicitar o próximo elemento na sequência.
O tipo de retorno de um iterador pode ser IEnumerable, IEnumerable<T>, IAsyncEnumerable<T>, IEnumerator ou IEnumerator<T>.
Para obter mais informações, consulte Iteradores.