пядь <t> не требует назначения локальной переменной. Это особенность?
Я замечаю, что следующее будет компилироваться и выполняться, даже если локальные переменные не инициализируются. Является ли это особенностью Span?
void Uninitialized()
{
Span<char> s1;
var l1 = s1.Length;
Span<char> s2;
UninitializedOut(out s2);
var l2 = s2.Length;
}
void UninitializedOut(out Span<char> s)
{}
Ответы
Ответ 1
Это похоже на проблему, вызванную ссылочными сборками, требуемую из-за того, что Span<T>
имеет инфраструктурные функции.
Это означает, что в контрольной сборке нет полей (отредактируйте: это не совсем так - см. Сноску).
struct
считается назначенной (для целей "определенного назначения"), если все поля назначены, и в этом случае компилятор видит, что "всем нулям нулевые поля присвоены: все хорошее - эта переменная назначается". Но компилятор, похоже, не знает о реальных полях, поэтому его вводят в заблуждение, позволяя что-то, что не является технически обоснованным.
Вы определенно не должны полагаться на это поведение красиво! Хотя в большинстве случаев .locals init
должен означать, что вы на самом деле не получаете ничего ужасного. Тем не менее, в настоящее время некоторые незавершенное, чтобы позволить людям, чтобы подавить .locals init
в некоторых случаях - Страшно подумать, что может произойти в этом случае здесь - особенно с Span<T>
работает так же, как ref T
- это может быть очень очень опасно, если поле действительно не инициализировано до нуля.
Интересно, что он уже может быть исправлен: см. Этот пример на sharplab. В качестве альтернативы, возможно, sharplab использует конкретную целевую структуру, а не контрольные сборки.
Редактирование: очень странно, если я загружаю ссылочную сборку в ildasm
или отражатель, я вижу:
.field private initonly object _dummy
который является поддельным полем в контрольной сборке, предназначенным для того, чтобы остановить это, но... похоже, что он не работает очень надежно прямо сейчас!
Обновление: видимо, разница здесь - тонкая, но известная проблема с компилятором, которая остается по соображениям совместимости; определенное назначение структур рассматривает частные поля типов, которые известны локально, но не рассматривает частные типы ссылочного типа типов во внешних сборках.
Ответ 2
Марк имеет отличный ответ. Я хотел немного рассказать об истории/контексте.
Прежде всего это определенно ошибка компилятора. По правилам определенного назначения этот локаль определенно не назначается, и любое использование должно быть ошибкой. К сожалению, эта ошибка трудно исправить по ряду причин:
- Эта ошибка устарела и восходит по крайней мере до С# 4.0. Это дает клиентам 7+ годы непреднамеренно зависеть от этого
- В BCL есть несколько структур, которые имеют эту основную структуру. Например, CancellationToken.
Те, что взяты вместе, означают, что это может привести к поломке большого количества существующего кода. Несмотря на это, команда С# попыталась исправить ошибку в С# 6.0, когда ошибка была намного моложе. Но попытка скомпилировать источник Visual Studio с этим исправлением показала, что опасения вокруг клиентов, зависящих от этой ошибки, были обоснованными: произошел ряд сбоев в работе. Достаточно, чтобы убедить нас, это окажет негативное влияние на значительный объем кода. Следовательно, исправление было отменено.
Вторая проблема здесь в том, что эта ошибка не была известна всем членам команды компилятора (по крайней мере до сегодняшнего дня). Было ~ 3 года с тех пор, как исправление было отменено и с тех пор немного перевернулось. Члены команды, которые проверяли, как мы создавали ссылочные сборки для Span<T>
не знали об этой ошибке и рекомендовали текущий проект на основе спецификации языка. Я один из тех разработчиков :(
Все еще обсуждая это, но, скорее всего, мы собираемся обновить стратегию ссылочной сборки для Span<T>
и других типов, чтобы она избегала этой ошибки компилятора.
Спасибо, что сообщили об этом. Извините за путаницу, вызванную :(
Ответ 3
Более или менее это по дизайну, так как оно сильно зависит, если основная struct
содержит все поля.
Этот код компилируется, например:
public struct MySpan<T>
{
public int Length => 1;
}
static class Program
{
static void Main(string[] args)
{
MySpan<char> s1;
var l1 = s1.Length;
}
}
Но этот код не делает:
public struct MySpan<T>
{
public int Length { get; }
}
static class Program
{
static void Main(string[] args)
{
MySpan<char> s1;
var l1 = s1.Length;
}
}
Похоже, что в этом случае структура дефолтна, и поэтому она не жалуется на недостающее назначение. То, что он не обнаруживает никаких полей, является ошибкой, как объясняется в ответе Марка.