Wszystko w mojej odpowiedzi to tylko moje przemyślenia na temat "dlaczego niebezpiecznie byłoby zezwolić na ten rodzaj dostępu". Nie wiem, czy to był prawdziwy powód, dla którego był ograniczony.
C# specyfikacja mówi, że inicjalizacji pola dzieje się w dziedzinie zamówień są zadeklarowane w klasie:
10.5.5.2. Instance field initialization
The variable initializers are executed in the textual order in which they appear in the class declaration.
Teraz, powiedzmy, że kod już wspomniano jest to możliwe - można wywołać metodę instancji z pola inicjalizacja. Może to zrobić następujący kod:
public class Progressor
{
private string _first = "something";
private string _second = GetMyString();
private string GetMyString()
{
return "this is really important string";
}
}
Jak dotąd tak dobrze. Ale bądźmy nadużywają tej władzy trochę:
public class Progressor
{
private string _first = "something";
private string _second = GetMyString();
private string _third = "hey!";
private string GetMyString()
{
_third = "not hey!";
return "this is really important string";
}
}
Więc _second
get zainicjowany przed _third
. GetMyString
działa, _third
get "nie hej!" przypisana wartość, ale później uruchamia się własna inicjalizacja pola i jest ustawiana na "" hej! ". Niezbyt przydatne, czytelne, prawda?
Można również użyć _third
ciągu GetMyString
metody:
public class Progressor
{
private string _first = "something";
private string _second = GetMyString();
private string _third = "hey!";
private string GetMyString()
{
return _third.Substring(0, 1);
}
}
Co można oczekiwać, aby być wartość _second
? Cóż, przed uruchomieniem inicjalizacji pola wszystkie pola otrzymują wartości domyślne. Dla string
będzie to null
, więc otrzymasz niespodziewany NullReferenceException
.
Tak więc projektanci zdecydowali, że łatwiej jest zapobiec takim błędom.
Można powiedzieć: OK, niech zabraknie dostępu do właściwości i metod wywoływania, ale pozwólmy na użycie pól, które zostały zadeklarowane powyżej tego, z którego chcesz uzyskać do niego dostęp. Coś jak:
public class Progressor
{
private string _first = "something";
private string _second = _first.ToUpperInvariant();
}
ale nie
public class Progressor
{
private string _first = "something";
private string _second = _third.ToUpperInvariant();
private string _third = "another";
}
To wydaje się użyteczne i bezpieczne. Ale wciąż jest sposób na jej nadużycie!
public class Progressor
{
private Lazy<string> _first = new Lazy<string>(GetMyString);
private string _second = _first.Value;
private string GetMyString()
{
// pick one from above examples
}
}
I wszystkie problemy z metodami powracają.
Jedyne, co przychodzi mi na myśl, to fakt, że gdy uruchomiona jest konkretna inicjalizacja pola, wszystkie inne pola mogą być zainicjalizowane lub nie. Uruchomienie w tym miejscu metody instancji, która dawałaby dostęp do wszystkich członków instancji, umożliwi dostęp do innych pól, które nie zostały jeszcze zainicjowane. To potencjalne źródło problemów. Dlaczego jest dozwolony w konstruktorze? Ponieważ wszystkie pola są inicjowane przed uruchomieniem kodu konstruktora. – MarcinJuraszek
@MarcinJuraszek To było moje zrozumienie, chociaż można by argumentować, że środowisko wykonawcze może po prostu zainicjować wszystkie pola do ich wartości domyślnych (tj. 0, null, itd.), A następnie uruchomić inicjalizacje pól, a następnie uruchomić odpowiedni konstruktor. – sdzivanovich
@sdzivanovich Dokładnie to się dzieje - wszystkie pola zostają zainicjowane wartościami domyślnymi, zanim nastąpi inicjalizacja.Ale nadal nie rozwiązuje problemu nieprzewidywalności kodu inicjalizacyjnego (zobacz moją odpowiedź). – MarcinJuraszek