Тот же код компилируется в проекте .NET 6, но показывает ошибку компиляции CS0177 в .NET 8. Почему?
Похоже, что правило будет действовать только для .net8, или некоторые правила изменились.
Поскольку кажется, что он работает в .NET 6...
Приложение работает в .NET 6 с выводом:
Hello, World!
Else: Bad Date Format! 'abc'
01.01.0001
Очевидно, что он использует значение структуры по умолчанию. Но почему-то для .NET 8 это уже не так (пока я не нашел этого в документации, по крайней мере, здесь)
Я тестировал это, создавая по 2 новых консольных проекта в VS для каждой версии, а также меняя целевой фреймворк в обоих проектах с тем же результатом.
CS0177 Выходной параметр «parameter» должен быть присвоен до того, как элемент управления покинет текущий метод. (без упоминания версий)
(Почему параметр Out можно оставить неназначенным в проектах, ориентированных на .NET Standard? это совсем не помогло, речь идет только о структурах, а не о версиях .net)
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
TryParse("abc", out DateOnly d);
Console.WriteLine(d);
Console.ReadKey();
}
public static bool TryParse(string s, out DateOnly dateOnly)
{
try
{
if (s is not null && s.Length == 8)
{
int yyyy = int.Parse(s[0..4]);
int mm = int.Parse(s[4..6]);
int dd = int.Parse(s[6..8]);
var date = new DateOnly(yyyy, mm, dd);
dateOnly = date;
return true;
}
else
{
Console.WriteLine("Else: Bad Date Format! '" + s + "'");
return false; //CS0177 in .net8
}
}
catch
{
Console.WriteLine("Catch: Bad Date Format! '" + s + "'");
return false; //CS0177 in .net8
}
}
}
Интересно, что я получаю сообщение об ошибке в .NET 6, если заменяю DateOnly
на DateTime
.
Альтернативно это может быть ошибка в C#12 (для .NET 8): если связанный вопрос re. structs верен, поскольку DateOnly
не имеет доступных полей экземпляра.
@Richard Я не могу воспроизвести на .NET 6 свою собственную структуру, в которой нет доступных полей экземпляра: dotnetfiddle.net/IzoE02
@Sinatr Это также член экземпляра в DateOnly
. static
не будет репрезентативным
Я подозреваю, что это проблема с эталонными сборками для .NET 6, но я пытаюсь их отследить.
К вашему сведению: воссоздается на C#13 (предварительная версия .NET 9). «Доступные» поля не являются правильным критерием; любое поле в структуре означает отсутствие неявного присваивания. Смотрите мой ответ для ссылок.
@Richard Это не воспроизводится на июньском main
на Sharplab: Sharplab.io/…
@canton7 Canton7 Я понятия не имею, на какую среду выполнения/язык нацелена Sharplib. И теперь мне ясно (поработав над спецификацией, см. мой ответ), что отсутствие ошибки в .NET 6 является ошибкой здесь. Учитывая, что .NET 6 почти закончился, я не думаю, что вы увидите исправление.
@Richard Один из разработчиков среды выполнения буквально сказал, что это исправлено в .NET 7 после того, как один из членов команды компилятора сказал, что это произошло из-за неправильной эталонной сборки.
@Ричард Я также не могу воспроизвести с помощью 9.0.100-preview.7
@canton7 Моя реконструкция (использовалась Visual Studio 17.12.0 Preview 1.0): gist.github.com/richardcox13/…
@Richard Когда вы говорите «пересоздает», я полагаю, вы имеете в виду, что у вас нет ошибок? Поскольку проблема в том, что ошибки нет, хотя она должна быть.
@canton7 Обратите внимание. под «пересоздать» я имею в виду, что получаю ошибку (т. е. заново создаю, что ошибка выдается из-за отсутствия сообщения о назначении .NET 6). Я думаю, что вы меняете смысл на противоположный!
@Ричард Верно. Да, вы получите ошибку в .NET 7 и более поздних версиях, поскольку они исправили ссылочные сборки в .NET 7.
@Ричард Я нашел исходную проблему, PR и коммит, который это исправил. Это была проблема с инструментом, который генерирует реф-сборки.
Это проблема ссылочных сборок в .NET 6. Она исправлена в .NET 7.
В C# структура считается «определенно назначенной», если она не имеет полей, подробности см. в этом ответе.
В .NET 6 ссылочная сборка для System.Runtime содержала определение для DateOnly и TimeOnly, в котором не было полей. Поэтому компилятор рассматривал ее как пустую структуру, которую не нужно было явно назначать.
Подробности см. в этом выпуске GitHub и в другом выпуске . Первоначально это было замечено на SO, в этом вопросе. Это было исправлено путем изменения инструмента, генерирующего ссылочные сборки, чтобы при необходимости включать в такие структуры фиктивное поле.
Git утверждает, что проблема была исправлена в этом коммите, но в диффе она не отображается. Я подозреваю, что где-то там есть злое слияние, которое его скрывает.
Я думаю, вы можете убрать слово «доступный» — я считаю, что это один из тех случаев, когда компилятор заглядывает под капот — IIRC, во многих реферальных сборках есть такие вещи, как private readonly bool dummy;
— редактировать: похоже на разные волны «исправления» поскольку это имело разное поведение - слово «доступный» было актуально для пункта 1 здесь, но теперь оно больше не актуально, поскольку (в основном) добавлены недостающие поля, я полагаю: github.com/dotnet/roslyn/issues/29319#issuecomment -414517139
@MarcGravell Да, хорошая мысль, спасибо, удалено
Не является ли это ошибкой в компиляторе, которую исправили путем изменения правил генерации эталонных сборок, а не проблемой самих эталонных сборок? Здесь сказано, что даже если в структуре есть поле, если это поле ссылочного типа, в другой сборке и приватное, то компилятор все равно его игнорирует.
@palapapa Интересное совпадение. DateOnly/TimeOnly не имеет поля ссылочного типа, поэтому я не думаю, что это применимо здесь? Также фактом является то, что ссылочные сборки были неправильными в .NET 6 и вообще не содержали никаких полей (в моем ответе есть ссылка), а компилятор следовал спецификации, когда ему был предоставлен тип без полей.
Работаем с последним доступным стандартом C# (я не думаю, что для C# 8 что-то существенное изменилось в спецификации параметров out
).
Внутри метода, как и в случае с локальной переменной, выходной параметр изначально считается неназначенным и должен быть обязательно присвоен перед использованием его значения. Каждый выходной параметр метода должен быть определенно назначен перед возвратом метода.
Как и ожидалось, необходимо задать out
параметры (об этом сказано дважды в одном абзаце).
Состояния определенного присвоения переменных экземпляра переменной struct_type отслеживаются как индивидуально, так и коллективно. В дополнение к правилам, описанным в §9.4.2, §9.4.3 и §9.4.4, к переменным struct_type и их переменным экземпляра применяются следующие правила:
- Переменная экземпляра считается определенно назначенной, если содержащая ее переменная struct_type считается определенно назначенной.
- Переменная struct_type считается определенно назначенной, если каждая из ее переменных экземпляра считается определенно назначенной.
В отличие от , на который ссылается Q , здесь нет ничего, что говорит о «доступных полях»: все они должны быть «определенно назначены».
Потому что в некоторых случаях SDK имеет «фасадные» сборки, которые отличаются от среды выполнения (например, DateOnly
документировано как наличие в System.Runtime
, но реализовано в System.Private.CoreLib
). Иногда (например, DateTime
) документированная сборка передается реальной реализации и, таким образом, разрешается компилятором, применяющим это правило. В других случаях (и, таким образом, со временем он изменился как деталь реализации) фасад разрешается только загрузчиком и JIT-компилятором во время выполнения, и, таким образом, компилятор не видит внутреннюю часть типа и не может видеть, есть ли поля, которые нужно назначить.
Для очевидной производительности в загрузчике/JIT и, безусловно, в AoT, случай, когда фасад вперед, вероятно, будет более распространенным сейчас, чем в прошлом.
TL/DR: необходимость явного назначения кажется правильной для любой структуры с любыми доступными или нет полями (и почему для структур внутренние изменения могут быть критическими изменениями).
Я думаю, пересылаемые сборки не имеют отношения к времени компиляции? Компилятор полагается только на ссылочные сборки. Так что «фасадные» сборки к этому не имеют отношения. И я не уверен, какое отношение здесь имеет JIT/AOT. Проблема заключается в ошибке компиляции в одной версии .NET, но не в других. Информация о «доступных полях» взята из «Переменная struct_type считается определенно назначенной, если каждая из ее переменных экземпляра считается определенно назначенной» - поэтому всегда назначается структура без полей. Это в сочетании со сборкой ref, в которой не было полей, привело к этой ошибке.
@canton7 для преобразования в собственный машинный код необходима «настоящая» реализация (а не какая-то ссылка, достаточная для компиляции в IL). Это будет JIT-компилятор во время выполнения или при выполнении AoT-компиляции.
Я знаю. Эта ошибка является ошибкой компилятора — она возникает во время компиляции. JIT не является причиной ошибки. Поэтому все, что связано с JIT или типами в сборках, разрешаемыми во время выполнения, не имеет и не может иметь отношения к этому вопросу.
Вероятно, это ошибка в .NET 6, но я не могу найти для нее соответствующую проблему.