TLDR: Jest to znany błąd od dawna. Pierwszy raz o tym napisałem w 2010 roku:
https://blogs.msdn.microsoft.com/ericlippert/2010/01/18/a-definite-assignment-anomaly/
Jest nieszkodliwy i możesz go bezpiecznie zignorować i pogratulować sobie znalezienia nieco niejasnego błędu.
Dlaczego kompilator nie wymusza tego, że Emailnależy go definitywnie przypisać?
Och, robi to w pewien sposób. Po prostu źle rozumie, jaki warunek implikuje, że zmienna jest definitywnie przypisana, jak zobaczymy.
Dlaczego ten kod kompiluje się, jeśli struktura jest tworzona w oddzielnym zestawie, ale nie kompiluje się, jeśli struktura jest zdefiniowana w istniejącym zestawie?
To sedno błędu. Błąd jest konsekwencją przecięcia się tego, w jaki sposób kompilator C # wykonuje określone sprawdzanie przypisania struktur i jak kompilator ładuje metadane z bibliotek.
Rozważ to:
struct Foo
{
public int x;
public int y;
}
// Yes, public fields are bad, but this is just
// to illustrate the situation.
void M(out Foo f)
{
OK, w tym momencie co wiemy? fjest aliasem zmiennej typu Foo, więc pamięć została już przydzielona i na pewno jest przynajmniej w stanie, w którym wyszła z alokatora pamięci. Jeśli obiekt wywołujący umieścił wartość w zmiennej, ta wartość jest dostępna.
Czego potrzebujemy? Wymagamy, aby fzostało to definitywnie przypisane w każdym punkcie, w którym kontrola Mnormalnie wychodzi . Więc można się spodziewać czegoś takiego:
void M(out Foo f)
{
f = new Foo();
}
które ustawia f.xi f.yich wartości domyślne. Ale co z tym?
void M(out Foo f)
{
f = new Foo();
f.x = 123;
f.y = 456;
}
To też powinno być w porządku. Ale tutaj jest kicker, dlaczego musimy przypisać wartości domyślne tylko po to, aby je zdmuchnąć chwilę później? Określony kontroler przypisania C # sprawdza, czy każde pole jest przypisane! To jest legalne:
void M(out Foo f)
{
f.x = 123;
f.y = 456;
}
I dlaczego nie powinno to być legalne? To typ wartości. fjest zmienną i zawiera już prawidłową wartość typu Foo, więc po prostu ustawmy pola i gotowe, prawda?
Dobrze. Więc jaki jest błąd?
Wykryty przez Ciebie błąd: kompilator C # nie jest ładowany w celu oszczędności kosztów metadanych dla prywatnych pól struktur znajdujących się w bibliotekach, do których się odwołuje . Te metadane mogą być ogromne i spowalniałyby kompilator przy bardzo małej wygranej, aby za każdym razem ładować je do pamięci.
A teraz powinieneś być w stanie wywnioskować przyczynę znalezionego błędu. Gdy kompilator sprawdza, czy parametr out jest definitywnie przypisany, porównuje liczbę znanych pól z liczbą pól, które zostały zdecydowanie zainicjowane, aw twoim przypadku wie tylko o zerowych polach publicznych, ponieważ metadane pola prywatnego nie zostały załadowane . Kompilator stwierdza: „wymagane zero pól, zainicjowanie zerowych pól, jesteśmy dobrzy”.
Jak powiedziałem, ten błąd istnieje od ponad dekady, a ludzie tacy jak ty czasami go odkrywają i zgłaszają. Jest nieszkodliwy i jest mało prawdopodobne, aby został naprawiony, ponieważ jego naprawa przynosi prawie zerową korzyść, ale wysoki koszt wydajności.
I oczywiście błąd nie wypowiada się na prywatne pola struktur, które są w kodzie źródłowym w twoim projekcie, ponieważ oczywiście kompilator ma już informacje o dostępnych polach prywatnych.