Инструкция fixed на основе шаблонаPattern-based fixed statement

СводкаSummary

Познакомьтесь с шаблоном, который позволит использовать типы для участия в инструкциях fixed.Introduce a pattern that would allow types to participate in fixed statements.

Причины для использованияMotivation

Язык предоставляет механизм для закрепления управляемых данных и получения собственного указателя на базовый буфер.The language provides a mechanism for pinning managed data and obtain a native pointer to the underlying buffer.

fixed(byte* ptr = byteArray)
{
   // ptr is a native pointer to the first element of the array
   // byteArray is protected from being moved/collected by the GC for the duration of this block 
}

Набор типов, которые могут участвовать в fixed, жестко определен и ограничен массивами и System.String.The set of types that can participate in fixed is hardcoded and limited to arrays and System.String. Прописано "специальные" типы не масштабируются, когда появились новые примитивы, такие как ImmutableArray<T>, Span<T>, Utf8String.Hardcoding "special" types does not scale when new primitives such as ImmutableArray<T>, Span<T>, Utf8String are introduced.

Кроме того, текущее решение для System.String полагается на достаточно жесткую API.In addition, the current solution for System.String relies on a fairly rigid API. Форма API подразумевает, что System.String является непрерывным объектом, который внедряет данные в кодировке UTF16 с фиксированным смещением из заголовка объекта.The shape of the API implies that System.String is a contiguous object that embeds UTF16 encoded data at a fixed offset from the object header. Такой подход был обнаружен с проблемой в нескольких предложениях, которые могут потребовать изменений в базовом макете.Such approach has been found problematic in several proposals that could require changes to the underlying layout. Было бы желательно иметь возможность переключиться на что-то более гибкое, которое отделяет System.String объект от его внутреннего представления в целях неуправляемого взаимодействия.It would be desirable to be able to switch to something more flexible that decouples System.String object from its internal representation for the purpose of unmanaged interop.

Подробный проектDetailed design

ШаблонPattern

Для "фиксированной" модели на основе шаблона необходимо:A viable pattern-based “fixed” need to:

  • Укажите управляемые ссылки, чтобы закрепить экземпляр и инициализировать указатель (желательно, чтобы это была та же ссылка).Provide the managed references to pin the instance and to initialize the pointer (preferably this is the same reference)
  • Передать однозначно тип неуправляемого элемента (т. е. "char" для "String")Convey unambiguously the type of the unmanaged element (i.e. “char” for “string”)
  • Предписывает поведение в пустом случае, если нет никаких ссылок на.Prescribe the behavior in "empty" case when there is nothing to refer to.
  • Не следует отправлять авторов API на решения по проектированию, которые повредит использование типа вне fixed.Should not push API authors toward design decisions that hurt the use of the type outside of fixed.

Я думаю, что описанный выше способ может быть удовлетворен путем распознавания специально именованного члена, возвращающего ссылочную ссылку: ref [readonly] T GetPinnableReference().I think the above could be satisfied by recognizing a specially named ref-returning member: ref [readonly] T GetPinnableReference().

Чтобы использовать инструкцию fixed, должны выполняться следующие условия.In order to be used by the fixed statement the following conditions must be met:

  1. Для типа предоставлен только один такой член.There is only one such member provided for a type.
  2. Возвращает по ref или ref readonly.Returns by ref or ref readonly. (readonly разрешено таким образом, чтобы авторы неизменяемых или доступных только для чтения типов могли реализовать шаблон, не добавляя записываемый API, который может использоваться в надежном коде).(readonly is permitted so that authors of immutable/readonly types could implement the pattern without adding writeable API that could be used in safe code)
  3. T является неуправляемым типом.T is an unmanaged type. (поскольку T* превращается в тип указателя.(since T* becomes the pointer type. Ограничение будет естественным, если/когда понятие "неуправляемый" развернуто.The restriction will naturally expand if/when the notion of "unmanaged" is expanded)
  4. Возвращает управляемые nullptr, если нет данных для закрепления, возможно, самый экономичный способ передать пустые данные.Returns managed nullptr when there is no data to pin – probably the cheapest way to convey emptiness. (Обратите внимание, что строка "" возвращает ссылку на "\ 0", так как строки завершаются нулем)(note that “” string returns a ref to '\0' since strings are null-terminated)

В качестве альтернативы #3 можно разрешить результат в пустых случаях не определен или зависит от реализации.Alternatively for the #3 we can allow the result in empty cases be undefined or implementation-specific. Тем не менее, это может сделать API более опасным и подверженным нарушениям и нежелательным затратам на совместимость.That, however, may make the API more dangerous and prone to abuse and unintended compatibility burdens.

ПереводTranslation

fixed(byte* ptr = thing)
{ 
    // <BODY>
}

превращается в следующий псевдокод (не все выражаются в C#)becomes the following pseudocode (not all expressible in C#)

byte* ptr;
// specially decorated "pinned" IL local slot, not visible to user code.
pinned ref byte _pinned;

try
{
    // NOTE: null check is omitted for value types 
    // NOTE: `thing` is evaluated only once (temporary is introduced if necessary) 
    if (thing != null)
    {
        // obtain and "pin" the reference
        _pinned = ref thing.GetPinnableReference();

        // unsafe cast in IL
        ptr = (byte*)_pinned;
    }
    else
    {
        ptr = default(byte*);
    }

    // <BODY> 
}
finally   // finally can be omitted when not observable
{
    // "unpin" the object
    _pinned = nullptr;
}

НедостаткиDrawbacks

  • Жетпиннаблереференце предназначен для использования только в fixed, но ничто не предотвращает его использование в надежном коде, поэтому разработчик должен учитывать это.GetPinnableReference is intended to be used only in fixed, but nothing prevents its use in safe code, so implementor must keep that in mind.

Альтернативные вариантыAlternatives

Пользователи могут ввести Жетпиннаблереференце или аналогичный элемент и использовать его в качествеUsers can introduce GetPinnableReference or similar member and use it as

fixed(byte* ptr = thing.GetPinnableReference())
{ 
    // <BODY>
}

Решение для System.String не существует, если требуется альтернативное решение.There is no solution for System.String if alternative solution is desired.

Неразрешенные вопросыUnresolved questions

  • [] Поведение в состоянии "пусто".[ ] Behavior in "empty" state. - nullptr или undefined? - nullptr or undefined ?
  • [] Следует ли учитывать методы расширения?[ ] Should the extension methods be considered ?
  • [] Если на System.Stringобнаруживается шаблон, должен ли он выиграть?[ ] If a pattern is detected on System.String, should it win over ?

Разработка собранийDesign meetings

Еще нет.None yet.