in - это сахар от ref. Любой in в итоге становится ref. Соотвественно относиться к нему следует таким же образом. Но все же стоит понимать разницу. in - это доступ только на чтение, ref - чтение и запись, out - только запись.
Пример:
void Method(in Vector3 vector) { vector.x = 123f; // будет ошибка доступа vector.Method(); // будет сделана копия vector, если метод не readonly }
А вот тот же пример с ref:
void Method(ref Vector3 vector) { vector.x = 123f; // ошибки не будет, значение будет изменено vector.Method(); // копии не будет }
Теперь рассмотрим вариант с возвращаемым значением по ссылке:
Vector3[] arr; public ref Vector3 Method(int index) { return ref arr[index]; }
Во-первых, возвращаемое значение не может быть из стэка, т.е. нельзя сделать вот так:
void Method() { ref var a = 123; }
Во-вторых, нужно понимать, что вернув ссылку - вы по сути получили указатель на память, т.е. не стоит менять тот участок, который вы используете:
ref var data = ref Method(index); System.Array.Resize(ref arr, ...); // Меняем данные, на которые держим ссылку data.x = 123f; // Изменит данные в предыдущем массиве, а не в новом, т.е. фактически изменений не будет
В-третьих, можно вернуть данные только для чтения:
public ref readonly Vector3 Method() {...}
только не путать с этим:
public readonly ref Vector3 Method() {...}
В первом варианте мы возвращаем данные, доступные для чтения, а во втором мы делаем метод, который не меняет объект, в котором объявлен.
Особенно прекрасно выглядит это:
public readonly ref readonly Vector3 Method() {...}
Лично я считаю, что это накосячили разрабы языка, т.к. readonly ref readonly - это дичь, почему было не использовать слово const?
Ну и последнее - нужно понимать, что in и ref - это ссылки, а значит void Method(in LargeStruct s) имеет смысл, чтобы избежать копирования, а вот void Method(in int val) не имеет, т.к. такой int будет передаваться по ссылке ref, но запрещать изменения, при этом ссылка будет занимать 8 байт.