Métodos (Guia de Programação 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 quaisquer argumentos de método necessários. Em C#, cada instrução executada é executada no contexto de um método.
O Main
método é o ponto de entrada para cada aplicativo C# e é chamado pelo Common Language Runtime (CLR) quando o programa é iniciado. Em um aplicativo que usa instruções de nível superior, o Main
método é gerado pelo compilador e contém todas as instruções de nível superior.
Nota
Este artigo discute métodos nomeados. Para obter informações sobre funções anônimas, consulte Expressões do Lambda.
Assinaturas de método
Os métodos são declarados em uma classe, struct ou interface especificando o nível de acesso, como public
ou private
, modificadores opcionais, como abstract
ou sealed
, o valor de retorno, o nome do método e quaisquer parâmetros de método. Estas partes juntas são 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 do método. No entanto, faz parte da assinatura do método ao determinar a compatibilidade entre um delegado e o método para o qual ele aponta.
Os parâmetros do método são colocados entre parênteses e separados por vírgulas. Parênteses vazios indicam que o método não requer parâmetros. Esta classe contém quatro métodos:
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 must implement this.
public abstract double GetTopSpeed();
}
Acesso ao método
Chamar um método em um objeto é como acessar um campo. Após o nome do objeto, adicione um ponto, o nome do método e parênteses. Os argumentos são listados entre parênteses e separados por vírgulas. Os métodos da Motorcycle
classe podem, portanto, ser chamados como no exemplo a seguir:
class TestMotorcycle : Motorcycle
{
public override double GetTopSpeed()
{
return 108.4;
}
static void Main()
{
TestMotorcycle moto = new TestMotorcycle();
moto.StartEngine();
moto.AddGas(15);
moto.Drive(5, 20);
double speed = moto.GetTopSpeed();
Console.WriteLine("My top speed is {0}", speed);
}
}
Parâmetros do método vs. argumentos
A definição do método especifica os nomes e tipos de quaisquer parâmetros que são necessários. Ao chamar o código chama 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 houver) usado no código de chamada não precisa ser o mesmo que o parâmetro nomeado definido no método. Por exemplo:
public void Caller()
{
int numA = 4;
// Call with an int variable.
int productA = Square(numA);
int numB = 32;
// Call with another int variable.
int productB = Square(numB);
// Call with an integer literal.
int productC = Square(12);
// Call with an expression that evaluates to int.
productC = Square(productA * 3);
}
int Square(int i)
{
// Store input argument in a local variable.
int input = i;
return input * input;
}
Passagem por referência vs. passagem por valor
Por padrão, quando uma instância de um tipo de valor é passada para um método, sua cópia é passada em vez da própria instância. Portanto, as alterações no argumento não têm efeito sobre a instância original no método de chamada. Para passar uma instância de tipo de valor por referência, use a ref
palavra-chave. Para obter mais informações, consulte Passando parâmetros de tipo de valor.
Quando um objeto de um tipo de referência é passado para um método, uma referência ao objeto é passada. Ou seja, o método recebe não o objeto em si, mas um argumento que indica a localização do objeto. Se você alterar um membro do objeto usando essa referência, a alteração será refletida no argumento no método de chamada, mesmo se você passar o objeto por valor.
Você cria um tipo de referência usando a palavra-chave class
, como mostra o exemplo a seguir:
public class SampleRefType
{
public int value;
}
Agora, se você passar um objeto baseado nesse tipo para um método, uma referência ao objeto será passada. O exemplo a seguir passa um objeto do tipo SampleRefType
para o método ModifyObject
:
public static void TestRefType()
{
SampleRefType rt = new SampleRefType();
rt.value = 44;
ModifyObject(rt);
Console.WriteLine(rt.value);
}
static void ModifyObject(SampleRefType obj)
{
obj.value = 33;
}
O exemplo faz essencialmente a mesma coisa que o exemplo anterior, na medida em que passa um argumento por valor para um método. Mas, como um tipo de referência é usado, o resultado é diferente. A modificação que é feita no ModifyObject
value
campo do parâmetro, obj
, também altera o value
campo do argumento, rt
no TestRefType
método. O TestRefType
método exibe 33 como a saída.
Para obter mais informações sobre como passar tipos de referência por referência e por valor, consulte Passando parâmetros de tipo de referência e tipos de referência.
Valores de retorno
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 void
for , o método pode retornar o valor usando a return
instrução. Uma instrução com a return
palavra-chave seguida por um valor que corresponda ao tipo de retorno retornará esse valor para o chamador do método.
O valor pode ser devolvido ao chamador por valor ou por referência. Os valores são retornados ao chamador por referência se a ref
palavra-chave for usada na assinatura do método e seguir cada return
palavra-chave. Por exemplo, a assinatura do método a seguir e a instrução return indicam que o método retorna uma variável nomeada estDistance
por referência ao chamador.
public ref double GetEstimatedDistance()
{
return ref estDistance;
}
A return
palavra-chave também interrompe a execução do método. Se o tipo de retorno for void
, uma return
instrução sem um valor ainda será útil para interromper a execução do método. Sem a palavra-chave return
, o método irá parar de ser executado quando chegar ao final do bloco de código. Os métodos com um tipo de retorno não vazio são necessários para usar a return
palavra-chave para retornar um valor. Por exemplo, esses dois métodos usam a return
palavra-chave para retornar inteiros:
class SimpleMath
{
public int AddTwoNumbers(int number1, int number2)
{
return number1 + number2;
}
public int SquareANumber(int number)
{
return number * number;
}
}
Para usar um valor retornado de um método, o método de chamada pode usar a própria chamada de método em qualquer lugar onde um valor do mesmo tipo seria suficiente. Você também pode atribuir o valor de retorno a uma variável. Por exemplo, os dois exemplos de código a seguir atingem o mesmo objetivo:
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);
Usar uma variável local, neste caso, result
, para armazenar um valor é opcional. Isso pode ajudar a legibilidade do código, ou pode ser necessário se você precisar armazenar o valor original do argumento para todo o escopo do método.
Para usar um valor retornado por referência de um método, você deve declarar uma variável local ref se pretender modificar seu valor. Por exemplo, se o Planet.GetEstimatedDistance
método retorna um Double valor por referência, você pode defini-lo como uma variável local ref com código como o seguinte:
ref double distance = ref Planet.GetEstimatedDistance();
Retornar uma matriz multidimensional de um método, M
, que modifica o conteúdo da matriz não é necessário se a função de chamada passou a matriz para M
. Você pode retornar a matriz resultante de M
para um bom estilo ou fluxo funcional de valores, mas isso não é necessário porque 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 método M
, quaisquer alterações no conteúdo da matriz são observáveis por qualquer código que tenha uma referência à matriz, conforme mostrado no exemplo a seguir:
static void Main(string[] args)
{
int[,] matrix = new int[2, 2];
FillMatrix(matrix);
// matrix is now full of -1
}
public static void FillMatrix(int[,] matrix)
{
for (int i = 0; i < matrix.GetLength(0); i++)
{
for (int j = 0; j < matrix.GetLength(1); j++)
{
matrix[i, j] = -1;
}
}
}
Métodos assíncronos
Usando o recurso assíncrono, você pode invocar métodos assíncronos sem usar retornos de chamada explícitos ou dividir manualmente seu código em vários métodos ou expressões lambda.
Se você marcar um método com o modificador async , poderá usar o operador await no método. Quando o controle atinge uma expressão await no método async, o controle retorna ao chamador e o progresso no método é suspenso até que a tarefa aguardada seja concluída. Quando a tarefa estiver concluída, a execução poderá ser retomada no método.
Nota
Um método assíncrono retorna ao chamador quando encontra o primeiro objeto aguardado 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 de Task<TResult>, TaskIAsyncEnumerable<T>ou void
. O void
tipo de retorno é usado principalmente para definir manipuladores de eventos, onde um tipo de void
retorno é necessário. Um método assíncrono que retorna void
não pode ser aguardado, e o chamador de um método de retorno de vazio não pode capturar exceções que o método lança. Um método assíncrono pode ter qualquer tipo de retorno semelhante a uma tarefa.
No exemplo a seguir, DelayAsync
é um método assíncrono que tem um tipo de retorno de Task<TResult>. DelayAsync
tem uma return
instrução que retorna um inteiro. Portanto, a declaração de método de DelayAsync
deve ter um tipo de retorno de Task<int>
. Como o tipo de retorno é Task<int>
, a avaliação da await
expressão em DoSomethingAsync
produz um inteiro como demonstra a seguinte instrução: int result = await delayTask
.
O Main
método é um exemplo de um método assíncrono que tem um tipo de retorno de Task. Ele vai para o DoSomethingAsync
método, e porque é expresso com uma única linha, ele pode omitir as async
palavras-chave e await
. Como DoSomethingAsync
é um método assíncrono, a tarefa para DoSomethingAsync
a chamada deve ser aguardada, como mostra a instrução a seguir: await DoSomethingAsync();
.
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 nenhum parâmetro ref ou out , mas pode chamar métodos que tenham esses parâmetros.
Para obter mais informações sobre métodos assíncronos, consulte Programação assíncrona com tipos de retorno async e await e Async.
Definições do corpo da expressão
É comum haver definições de método que simplesmente 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 tais 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 retorna void
ou é um método assíncrono, então o corpo do método deve ser uma expressão de instrução (igual ao lambdas). Para propriedades e indexadores, eles devem ser somente leitura e você não usa a get
palavra-chave accessor.
Iteradores
Um iterador executa uma iteração personalizada sobre uma coleção, como uma lista ou uma matriz. Um iterador usa a instrução yield return para retornar cada elemento, um de cada vez. Quando uma yield return
instrução é alcançada, o local atual no código é lembrado. A execução é reiniciada a partir desse local quando o iterador é chamado na próxima vez.
Você chama um iterador do código do cliente usando uma instrução foreach .
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.
Especificação da linguagem C#
Para obter mais informações, consulte a Especificação da linguagem C#. A especificação da linguagem é a fonte definitiva para a sintaxe e o uso do C#.