Ref-поля в C#11
Ключевое слово
ref указывает, что переменная передаётся по ссылке. Это позволяет вызываемой функции изменить значение переменной в вызывающем её коде. C# поддерживает ref для аргумента метода или возврата метода, а также для локальной переменной:int i = 10;В этом примере параметр j и локальная переменная k являются псевдонимами для переменной i. Когда вы получаете их значения, читается i, а когда устанавливаете - устанавливается значение i. Ref-переменные могут указывать как на память на стеке, что подразумевает строгие правила области видимости, так и на память в куче, которой управляет сборщик мусора.
Foo(ref i);
ref int Foo(ref int j)
{
ref int k = ref i;
return ref j;
}
Компилятор сохраняет область видимости, т.е. разрешено возвращать
ref j, потому что компилятор знает, что переменная, на которую ссылается параметр, всё ещё будет в области видимости. Компилятор не позволит вам вернуть ссылку на k, потому что локальная ссылка может указывать на стековую память, которая выйдет за пределы области видимости при возврате из метода.C#8 представил ref-структуры и использовал их для реализации Span. Тип Span — это абстракция в стеке для области памяти стека или кучи. Для представления памяти Span нужны два поля: ref-поле, указывающее на первый элемент памяти, и поле для хранения длины блока памяти. До C#11 ref-поле было внутренней реализацией среды выполнения.
В C#11 ref-поля открыты для общего использования. Они позволяют вам определить ваши собственные ref-структуры имеющие ref-поля. Например, вы можете создать тип, читающий информацию из большой структуры, которая может находиться в стеке, в куче или даже в неуправляемой памяти:
struct MyStruct {
/* большая структура */
}
ref struct StructReader
{
ref MyStruct _value;
public StructReader(ref MyStruct value)
=> _value = ref value;
public void ReadValue(Span<byte> value)
{ … }
…
}
При передаче ссылок в методы ref-структуры компилятор гарантирует, что переменные, на которые вы ссылаетесь, не выйдут за пределы области видимости раньше, чем сама структура. В противном случае ref-структура могла бы ссылаться на переменные вне области видимости:void Process(ref StructReader reader)Здесь будет выдана ошибка CS8352: Cannot use variable 'buffer' in this context because it may expose referenced variables outside of their declaration scope (Невозможно использовать переменную 'buffer' в этом контексте, потому что она может раскрыть переменные, на которые она ссылается, за пределы области их объявления).
{
Span<byte> buffer = stackalloc byte[10];
reader.ReadSomeValue(buffer);
}
Вы можете указать компилятору разрешить передачу этих переменных, добавив ключевое слово scoped. Тогда компилятор запретит сохранять такие параметры в структуре:
public void ReadSomeValue(Т.е. ключевое слово scoped указывает компилятору обращаться с аргументом value так же, как если бы он был локальной переменной в методе.
scoped Span<byte> value
)
{ … }
Источник: https://developers.redhat.com/articles/2023/01/11/5-new-advanced-features-improving-c-11#ref_fields