В чем смысл модификатора in для классов

в C# 7.2 вводит in модификатор для параметров, который имеет смысл для структур и, в частности, для структур только для чтения.

также разрешено использовать его для ссылочного типа

void Method(in StringBuilder value) { }

как ссылочные типы передаются по ссылке по умолчанию, является in в приведенном выше примере просто избыточный модификатор?

value = null запрещено при использовании in, означает ли это, что он щадит также копию ссылочного адреса, просто передача исходной ссылки на местоположение кучи и блокирование изменений?

3 ответов


in компилируется в IL точно так же, как ref, за исключением in аргумент помечен как .

что означает in ведет себя точно так же, как ref, но компилятор (не среда выполнения) обеспечивает, чтобы вы не присваивали значение


из того, что я понимаю из официальная документация, это означает, что аргументы, переданные методу не будут изменены внутри самого метода:

на in ключевое слово указывает, что вы передаете параметр по ссылке и вызываемый метод не изменяет переданное ему значение.

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


в то время как два других ответа верны, что in параметры заканчиваются как ref параметры в результирующем IL следует соблюдать осторожность с утверждением, что это предотвращает копирование значения. Это справедливо только для структур readonly.

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

using System;

public struct S1
{
    public int A;

    public void ChangeA(int a) => A = a;
}

public static class Program
{
    static void Main()
    {
        var s1 = new S1 { A = 1 };
        S1Foo(in s1);
        Console.WriteLine(s1.A);
    }

    private static void S1Foo(in S1 s) => s.ChangeA(2);
}

так как мы проезжаем s1 по ссылке можно разумно предположить, что S1Foo при использовании ChangeA затем изменит содержимое s1. этого не происходит, хотя. Причина в том, что s1 значение копируется и копия передается по ссылке, чтобы предотвратить такие изменения структур через in параметры.

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

public static class Program
{
    private static void Main()
    {
        S1 s = default(S1);
        s.A = 1;
        S1 s2 = s;
        Program.S1Foo(ref s2);
        Console.WriteLine(s2.A);
    }

    private static void S1Foo([IsReadOnly] [In] ref S1 s)
    {
        S1 s2 = s;
        s2.ChangeA(2);
    }
}

однако, если мы напишем аналогичный код, используя readonly struct, то никакое копирование не происходит. Я говорю подобное, поскольку невозможно написать тот же код, что и поля и свойство должно быть прочитано только в структуре readonly (ключ находится в имени):

using System;

public readonly struct S2
{
    private readonly int _a;
    public int A => _a;
    public S2(int a) => _a = a;

    public void ChangeA(int a) { }
}

public static class Program
{
    static void Main()
    {
        var s2 = new S2(1);
        S2Foo(in s2);
        Console.WriteLine(s2.A);
    }

    private static void S2Foo(in S2 s) => s.ChangeA(2);
}

затем в результирующем IL.

таким образом:

  1. in эффективно readonly ref,
  2. значение (или ссылка) передается по ссылке,
  3. компилятор предотвращает изменение полей и свойств этой ссылки, чтобы помочь обеспечить ее только для чтения,
  4. для дальнейшего исполнения только для чтения характер параметра, а затем структуры не только для чтения копируются перед передачей ссылки на копию в метод. Это не происходит для структур readonly.