Модификатор параметров in (справочник по C#)

Ключевое слово in вызывает передачу аргументов по ссылке, но гарантирует, что аргумент не будет изменен. В результате этот формальный параметр становится псевдонимом для аргумента, который должен представлять собой переменную. Другими словами, любая операция в параметре осуществляется с аргументом. Оно аналогично ключевым словам ref и out, за исключением того, что аргументы in не могут быть изменены вызываемым методом. В то время как аргументы ref могут быть изменены, аргументы out должны быть изменены вызывающим объектом, и эти изменения отслеживаются в вызывающем контексте.

int readonlyArgument = 44;
InArgExample(readonlyArgument);
Console.WriteLine(readonlyArgument);     // value is still 44

void InArgExample(in int number)
{
    // Uncomment the following line to see error CS8331
    //number = 19;
}

Предыдущий пример показывает, что модификатор in обычно не требуется в месте вызова. Он нужен только в объявлении метода.

Примечание

Ключевое слово in также можно использовать с параметром универсального типа для указания на то, что тип параметра является контравариантным, в рамках оператора foreach или предложения join в запросе LINQ. Дополнительные сведения об использовании ключевого слова in в таких контекстах см. в разделе in, где приведены все соответствующие ссылки.

Переменные, передаваемые в качестве аргументов in, требуется инициализировать перед передачей в вызов метода. Но вызванный метод не может присвоить значение или изменить аргумент.

Модификатор параметра in доступен в C# 7.2 и более поздних версиях. Предыдущие версии создают ошибку компилятора CS8107 ("Функция "Ссылки только для чтения" недоступна в C# 7.0. Используйте языковую версию 7.2 или выше"), чтобы настроить языковую версию компилятора, см. Выбор версии языка C#.

Ключевые слова in, ref и out не считаются частью сигнатуры метода для разрешения перегрузки. Таким образом, методы не могут быть перегружены, если единственное различие состоит в том, что один метод принимает аргумент ref или in, а другой — out. Следующий код, например, компилироваться не будет.

class CS0663_Example
{
    // Compiler error CS0663: "Cannot define overloaded
    // methods that differ only on in, ref and out".
    public void SampleMethod(in int i) { }
    public void SampleMethod(ref int i) { }
}

Перегрузка, основанная на наличии in, допустима:

class InOverloads
{
    public void SampleMethod(in int i) { }
    public void SampleMethod(int i) { }
}

Правила разрешения перегрузки

Разобраться в правилах разрешения перегрузки для методов по значению и аргументам in можно, поняв основания для применения аргументов in. Определение методов с помощью параметров in является потенциальной оптимизацией производительности. Некоторые аргументы типа struct могут иметь большой размер, и при вызове методов в ограниченных циклах или критических путях кода стоимость копирования этих структур имеет определяющее значение. Методы объявляют параметры in, чтобы указать, что аргументы можно безопасно передавать по ссылке, так как вызываемый метод не изменяет состояние этого аргумента. Передача этих аргументов по ссылке позволяет избежать (потенциально) дорогого копирования.

Указывать in для аргументов в месте вызова обычно необязательно. Нет семантического различия между передачей аргументов по значению и передачей их по ссылке с помощью модификатора in. Модификатор in в месте вызова является необязательным, так как не нужно указывать, что значение аргумента может изменяться. Вы явным образом добавляете модификатор in в место вызова, чтобы аргумент передавался по ссылке, а не по значению. Явное использование in приводит к двум результатам.

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

Во-вторых, указав in, вы объявляете о намерении передать аргумент по ссылке. Аргумент, используемый с in, должен представлять расположение, на которое можно сослаться напрямую. Такие же общие правила применяются к аргументам out и ref: вы не можете использовать константы, обычные свойства или другие выражения, возвращающие значения. В противном случае пропуск in в месте вызова сообщает компилятору, что вы разрешите ему создать временную переменную для передачи с помощью ссылки на метод, доступной только для чтения. Компилятор создает временную переменную, чтобы преодолеть некоторые ограничения для аргументов in:

  • Временная переменная позволяет использовать константы времени компиляции, например параметры in.
  • Временная переменная позволяет использовать свойства или другие выражения для параметров in.
  • Временная переменная позволяет использовать аргументы, где выполняется неявное преобразование из типа аргумента в тип параметра.

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

Эти правила проиллюстрированы в следующем коде:

static void Method(in int argument)
{
    // implementation removed
}

Method(5); // OK, temporary variable created.
Method(5L); // CS1503: no implicit conversion from long to int
short s = 0;
Method(s); // OK, temporary int created with the value 0
Method(in s); // CS1503: cannot convert from in short to in int
int i = 42;
Method(i); // passed by readonly reference
Method(in i); // passed by readonly reference, explicitly using `in`

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

static void Method(int argument)
{
    // implementation removed
}

static void Method(in int argument)
{
    // implementation removed
}

Method(5); // Calls overload passed by value
Method(5L); // CS1503: no implicit conversion from long to int
short s = 0;
Method(s); // Calls overload passed by value.
Method(in s); // CS1503: cannot convert from in short to in int
int i = 42;
Method(i); // Calls overload passed by value
Method(in i); // passed by readonly reference, explicitly using `in`

Аргумент передается по ссылке только в последнем вызове метода.

Примечание

Для простоты предыдущий код использует int в качестве типа аргумента. Так как по размеру int не больше ссылки на большинстве современных компьютеров, не имеет смысла передавать один int в качестве ссылки, доступной только для чтения.

Ограничения для параметров in

Ключевые слова in, ref и out запрещено использовать для следующих типов методов.

  • Асинхронные методы, которые определяются с помощью модификатора async.
  • Методы итератора, которые включают оператор yield return или yield break.
  • Первый аргумент метода расширения не может иметь модификатор in, если только этот аргумент не является структурой.
  • Первый аргумент метода расширения, в котором этот аргумент является универсальным типом (даже если этот тип ограничен структурой).

Дополнительные сведения о модификаторе in, отличии от ref и out см в статье Написание безопасного эффективного кода.

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

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