Поделиться через


Методы (руководство по программированию на C#)

Метод — это блок кода, содержащий ряд инструкций. Программа инициирует выполнение инструкций, вызывая метод и указывая все аргументы, необходимые для этого метода. В C# все инструкции выполняются в контексте метода.

Метод Main является точкой входа для каждого приложения C# и вызывается средой CLR при запуске программы. В приложении, использующего инструкции верхнего уровня, Main метод создается компилятором и содержит все инструкции верхнего уровня.

Примечание.

В этой статье рассматриваются именованные методы. Дополнительные сведения об анонимных функциях см. в статье Лямбда-выражения.

Сигнатуры методов

Методы объявляются в классе, структуре или интерфейсе путем указания уровня доступа, например или необязательных модификаторов, таких как publicprivate или abstractsealedвозвращаемое значение, имя метода и любые параметры метода. Эти части вместе являются сигнатурой метода.

Это важно

Тип возврата метода не является частью сигнатуры метода в целях перегрузки метода. Однако он является частью сигнатуры метода при определении совместимости между делегатом и методом, на который он указывает.

Параметры метода заключены в скобки и разделены запятыми. Пустые скобки указывают, что метод не требует параметров. Этот класс содержит четыре метода:

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();
}

Доступ к методу

Вызов метода для объекта похож на доступ к полю. После имени объекта добавьте период, имя метода и круглые скобки. Аргументы перечислены в скобках и разделены запятыми. Поэтому методы Motorcycle класса можно вызывать следующим образом:

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 {speed}");
    }
}

Параметры метода и аргументы

Определение метода задает имена и типы всех необходимых параметров. Когда вызывающий код вызывает метод, он предоставляет конкретные значения, называемые аргументами, для каждого параметра. Аргументы должны быть совместимы с типом параметра, но имя аргумента (если есть), используемое в вызывающем коде, не должно совпадать с параметром, указанным в методе. Рассмотрим пример.

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;
}

Передача по ссылке и передача по значению

По умолчанию, когда экземпляр типа значения передается методу, его копия передается вместо самого экземпляра. Поэтому изменения аргумента не оказывают никакого влияния на первоначальный экземпляр в методе, в котором он вызывается. Чтобы передать экземпляр типа значения по ссылке, используйте ключевое слово ref. Дополнительные сведения см. в разделе "Передача Value-Type параметров".

Когда объект ссылочного типа передается методу, передается ссылка на объект. То есть метод получает не сам объект, а аргумент, указывающий расположение объекта. При изменении элемента объекта с помощью этой ссылки изменение отражается в аргументе в вызывающем методе, даже если объект передается по значению.

Вы создаете ссылочный тип с помощью ключевого class слова, как показано в следующем примере:

public class SampleRefType
{
    public int value;
}

Теперь, если передать объект, основанный на этом типе, методу, передается ссылка на объект. В следующем примере объект типа SampleRefType передается методу 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;
}

Пример делает по сути то же самое, что и предыдущий, тем, что передает аргумент по значению методу. Но, поскольку используется ссылочный тип, результат отличается. Изменение, которое выполняется в ModifyObject, в поле value параметра obj, также изменяет поле value аргумента rt в методе TestRefType. Метод TestRefType отображает 33 в качестве выходных данных.

Дополнительные сведения о том, как передавать ссылочные типы по ссылке и по значению, см. в разделах Передача Reference-Type параметров и Ссылочные типы.

Возвращаемые значения

Методы могут возвращать значение вызывающему. Если возвращаемый тип (тип, указанный перед именем метода), не равен void, метод может вернуть значение, используя оператор return. Оператор с return ключевым словом, за которым следует значение, соответствующее возвращаемому типу, вернет это значение вызывающему методу.

Значение можно вернуть вызывающему объекту по значению или по ссылке. Значения возвращаются вызывающей стороне по ссылке, если ключевое слово ref используется в сигнатуре метода и следует за каждым ключевым словом return. Например, следующая сигнатура метода и инструкция return указывают, что метод возвращает переменную с именем estDistance по ссылке на вызывающий объект.

public ref double GetEstimatedDistance()
{
    return ref estDistance;
}

Ключевое слово return также останавливает выполнение метода. Если тип возврата — void, инструкцию return без значения по-прежнему можно использовать для завершения выполнения метода. return Без ключевого слова метод перестанет выполняться, когда он достигнет конца блока кода. Методы с типом возвращаемого значения, отличного от void, требуются для использования return ключевого слова для возврата значения. Например, в следующих двух методах ключевое слово return используется для возврата целочисленных значений.

class SimpleMath
{
    public int AddTwoNumbers(int number1, int number2)
    {
        return number1 + number2;
    }

    public int SquareANumber(int number)
    {
        return number * number;
    }
}

Чтобы использовать значение, возвращаемое из метода, вызывающий метод может использовать вызов метода там, где было бы достаточно значения того же типа. Можно также назначить возвращаемое значение переменной. Например, следующие два примера кода выполняют одну и ту же задачу:

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для хранения значения является необязательным. Это может помочь удобочитаемости кода или может потребоваться, если необходимо сохранить исходное значение аргумента для всей области метода.

Чтобы использовать значение, возвращаемое ссылкой из метода, необходимо объявить локальную переменную ref, если планируется изменить его значение. Например, если метод Planet.GetEstimatedDistance возвращает значение по ссылке, его можно определить как локальную переменную ссылочного типа Double с помощью кода, подобного следующему:

ref double distance = ref Planet.GetEstimatedDistance();

Возврат многомерного массива из метода, Mкоторый изменяет содержимое массива, не требуется, если вызывающая функция передает массив в M. Можно вернуть полученный массив из M для улучшения стиля или функционального потока значений, но это не обязательно, так как C# передает все ссылочные типы по значению, а значение ссылки на массив — это указатель на массив. В методе Mлюбые изменения содержимого массива отслеживаются любым кодом, который имеет ссылку на массив, как показано в следующем примере:

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;
        }
    }
}

Асинхронные методы

С помощью функции async можно вызывать асинхронные методы, не прибегая к использованию явных обратных вызовов или ручному разделению кода между несколькими методами или лямбда-выражениями.

Если пометить метод с помощью модификатора async, можно использовать в этом методе инструкцию await. Когда элемент управления достигает выражения await в асинхронном методе, элемент управления возвращается вызывающему объекту и ход выполнения метода приостанавливается до тех пор, пока ожидающая задача не завершится. После завершения задачи выполнение в методе может быть возобновлено.

Примечание.

Асинхронный метод возвращает управление вызывающему объекту, когда он встречает первый ожидаемый объект, выполнение которого еще не завершено, или когда выполнение асинхронного метода доходит до конца, в зависимости от того, что произойдет раньше.

Асинхронный метод обычно имеет тип возвращаемого значения Task<TResult>, Task, IAsyncEnumerable<T> или void. Тип возвращаемого значения void в основном используется для определения обработчиков событий, где требуется возвращать тип void. Асинхронный метод, который возвращает тип void, не может быть ожидающим. Вызывающий объект метода, возвращающего значение типа void, не может перехватывать исключения, которые выдает этот метод. Асинхронный метод может иметь любой тип возвращаемой задачи.

В следующем примере DelayAsync — это асинхронный метод, который имеет тип возвращаемого значения Task<TResult>. У DelayAsync есть оператор return, который возвращает целое число. Поэтому объявление DelayAsync метода должно иметь возвращаемый тип Task<int>. Так как возвращаемый тип - Task<int>, вычисление выражения await в DoSomethingAsync создает целое число, как показано в следующем утверждении: int result = await delayTask.

Этот Main метод является примером асинхронного метода, имеющего возвращаемый тип Task. Он переходит к методу DoSomethingAsync, и поскольку он представлен в одной строке, может опустить ключевые слова async и await. Поскольку DoSomethingAsync является асинхронным методом, выполнение вызова DoSomethingAsync должно ожидаться, как показано в следующем выражении: 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

Асинхронный метод не может объявлять какие-либо параметры ref или out, но может вызывать методы, имеющие такие параметры.

Дополнительные сведения об асинхронных методах см. в разделах Асинхронное программирование с использованием ключевых слов async и await (C#) и Типы возвращаемых значений асинхронных операций.

Определения текста выражения

Обычно используются определения методов, которые просто сразу возвращают результат выражения или имеют единственное выражение в качестве тела метода. Существует ярлык синтаксиса для определения таких методов с помощью =>:

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);

Если метод возвращает void или является асинхронным методом, текст метода должен быть выражением инструкции (аналогично лямбда-выражениям). Для свойств и индексаторов необходимо, чтобы они были только для чтения, и вы не используете ключевое слово метода доступа get.

Итераторы

Итератор выполняет настраиваемую итерацию по коллекции, например по списку или массиву. Итератор использует инструкцию yield return для возврата всех элементов по одному. После достижения инструкции запоминается текущее yield return расположение в коде. Выполнение перезапускается с этой позиции, когда итератор вызывается в следующий раз.

Вы вызываете итератор из клиентского кода с помощью инструкции foreach .

Тип возвращаемого итератора может быть IEnumerable, IEnumerable<T>, IAsyncEnumerable<T>, IEnumeratorили IEnumerator<T>.

Дополнительные сведения см. в разделе Итераторы.

Спецификация языка C#

Дополнительные сведения см. в спецификации языка C#. Спецификация языка является авторитетным источником синтаксиса и использования языка C#.

См. также