Zauważyłem dziwne zachowanie w kodzie podczas przypadkowego komentowania linii w funkcji podczas przeglądania kodu. Bardzo trudno było to odtworzyć, ale przedstawię tutaj podobny przykład.
Mam tę klasę testową:
public class Test
{
public void GetOut(out EmailAddress email)
{
try
{
Foo(email);
}
catch
{
}
}
public void Foo(EmailAddress email)
{
}
}
nie ma przypisania do wiadomości e-mail, w GetOut
którym zwykle zgłaszany jest błąd:
Do parametru wyjściowego „e-mail” należy przypisać, zanim kontrola opuści bieżącą metodę
Jednak jeśli EmailAddress znajduje się w strukturze w oddzielnym zestawie, nie powstaje błąd i wszystko się kompiluje.
public struct EmailAddress
{
#region Constructors
public EmailAddress(string email)
: this(email, string.Empty)
{
}
public EmailAddress(string email, string name)
{
this.Email = email;
this.Name = name;
}
#endregion
#region Properties
public string Email { get; private set; }
public string Name { get; private set; }
#endregion
}
Dlaczego kompilator nie wymusza przypisania tego adresu e-mail? 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?
struct Dog{}
, wszystko jest w porządku.Odpowiedzi:
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.
Och, robi to w pewien sposób. Po prostu źle rozumie, jaki warunek implikuje, że zmienna jest definitywnie przypisana, jak zobaczymy.
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:
OK, w tym momencie co wiemy?
f
jest aliasem zmiennej typuFoo
, 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
f
zostało to definitywnie przypisane w każdym punkcie, w którym kontrolaM
normalnie wychodzi . Więc można się spodziewać czegoś takiego:które ustawia
f.x
if.y
ich wartości domyślne. Ale co z tym?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:
I dlaczego nie powinno to być legalne? To typ wartości.
f
jest zmienną i zawiera już prawidłową wartość typuFoo
, 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.
źródło
System.TimeSpan
zamiast tego, pojawiają się błędy:error CS0269: Use of unassigned out parameter 'email'
ierror CS0177: The out parameter 'email' must be assigned to before control leaves the current method
. Jest tylko jedno pole niestatyczneTimeSpan
, mianowicie_ticks
. To jestinternal
do jego złożenia mscorlib. Czy ten zespół jest wyjątkowy? To samo dotyczySystem.DateTime
, a jego pole toprivate
Choć wygląda jak błąd, ma jakiś sens.
„Brakujący błąd” pojawia się tylko podczas korzystania z biblioteki klas. A biblioteka klas mogła zostać napisana w innym języku .net, np. VB.Net. „Śledzenie określonego przypisania” jest funkcją C #, a nie frameworka.
Ogólnie rzecz biorąc, nie sądzę, że to błąd, ale nie wiem o autorytatywnym stwierdzeniu w tym zakresie.
źródło
default(T)
). Więc nie ma naruszenia bezpieczeństwa pamięci ani czegoś podobnego.