The same code compiles in a .NET 6 project, but it shows the compile error CS0177 in .NET 8. Why?
It seems like the rule would only be valid for .NET 8 or some rules have changed.
Since it seems to work in .NET 6...
The application runs in .NET 6 with the output:
Hello, World!
Else: Bad Date Format! 'abc'
01.01.0001
So clearly it uses the default value of the struct. But somehow this is not anymore the case for .NET 8 (I didn't find this documented so far, at least not here).
I tested it with creating two new console projects in Visual Studio for each version, and also by changing the target framework in both projects with the same result.
CS0177 The out parameter 'parameter' must be assigned to before control leaves the current method (does not mention versions)
(Why can an Out Parameter be left unassigned in projects targeting .NET Standard?. This did not help at all. It talks only about structs, not .NET versions)
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
}
}
}


This is a problem with the reference assemblies in .NET 6. It was fixed in .NET 7.
In C#, a struct is considered "definitely assigned" if it doesn't have any fields. See this answer for details.
In .NET 6, the reference assembly for System.Runtime contained a definition for DateOnly and TimeOnly which did not have any fields. The compiler therefore treated it as an empty struct, which didn't need to be explicitly assigned.
See this GitHub issue and this other one for details. This was originally spotted on Stack Overflow, in this question. It was fixed by changing the tooling which generates the reference assemblies, to include a dummy field in such structs where necessary.
Git claims that the issue was fixed in this commit, but it's not showing up in the diff. I suspect that there's an evil merge in there somewhere which is hiding it.
Working through the latest available C# standard (for C# 8, I don't think anything significant has changed specification-wise for out parameters).
Within a method, just like a local variable, an output parameter is initially considered unassigned and shall be definitely assigned before its value is used. Every output parameter of a method shall be definitely assigned before the method returns.
As expected out parameters must be assigned (so much it is said twice in the same paragraph).
The definite-assignment states of instance variables of a struct_type variable are tracked individually as well as collectively. In additional to the rules described in §9.4.2, §9.4.3, and §9.4.4, the following rules apply to struct_type variables and their instance variables:
- An instance variable is considered definitely assigned if its containing struct_type variable is considered definitely assigned.
- A struct_type variable is considered definitely assigned if each of its instance variables is considered definitely assigned.
Unlike the referenced Q there is nothing here that talks about "accessible fields": they all need to be "definitely assigned".
Because in some cases the SDK has "façade" assemblies which are different to the runtime (eg. DateOnly is documented to be in System.Runtime but is implemented in System.Private.CoreLib). Sometimes (like DateTime) the documented assembly forwards to the real implementation and thus is resolved by the compiler enforcing this rule. In other cases (and thus has changed over time as an implementation detail) the façade is only resolved by the loader and JIT compiler at runtime and thus the compiler does not see the internals of the type and cannot see there are fields to be assigned.
For obvious performance in the loader/JIT, and definitely for AoT, the case where the façade forwards is likely to be more common now than in the past.
TL/DR: needing the explicit assignment seems to be correct for any struct with any fields accessible or not (and why, for structs, internal changes can be a breaking changes).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With