Poniższy kod powoduje użycie nieprzypisanej zmiennej lokalnej „numberOfGroups” :
int numberOfGroups;
if(options.NumberOfGroups == null || !int.TryParse(options.NumberOfGroups, out numberOfGroups))
{
numberOfGroups = 10;
}
Jednak ten kod działa dobrze (chociaż ReSharper twierdzi, że = 10
jest zbędny):
int numberOfGroups = 10;
if(options.NumberOfGroups == null || !int.TryParse(options.NumberOfGroups, out numberOfGroups))
{
numberOfGroups = 10;
}
Czy coś mi brakuje, czy kompilator nie lubi mojego ||
?
Zawęziłem to do dynamic
powodowania problemów ( options
była to zmienna dynamiczna w moim powyższym kodzie). Pozostaje pytanie, dlaczego nie mogę tego zrobić ?
Ten kod nie kompiluje się:
internal class Program
{
#region Static Methods
private static void Main(string[] args)
{
dynamic myString = args[0];
int myInt;
if(myString == null || !int.TryParse(myString, out myInt))
{
myInt = 10;
}
Console.WriteLine(myInt);
}
#endregion
}
Jednak ten kod robi :
internal class Program
{
#region Static Methods
private static void Main(string[] args)
{
var myString = args[0]; // var would be string
int myInt;
if(myString == null || !int.TryParse(myString, out myInt))
{
myInt = 10;
}
Console.WriteLine(myInt);
}
#endregion
}
Nie zdawałem sobie sprawy, dynamic
że będzie to miało znaczenie.
out
parametru jako danych wejściowychout
parametru. Z pewnością warto zastanowić się, jaki kod pomocniczy powinien wygenerować kompilator, aby uniknąć problemu lub jeśli jest to w ogóle możliwe.Odpowiedzi:
Jestem prawie pewien, że jest to błąd kompilatora. Niezłe znalezisko!
Edycja: to nie jest błąd, jak demonstruje Quartermeister; Dynamic może zaimplementować dziwny
true
operator, który może spowodować,y
że nigdy nie zostanie zainicjowany.Oto minimalna reprodukcja:
Nie widzę powodu, dla którego miałoby to być nielegalne; jeśli zamienisz dynamic na bool, kompiluje się dobrze.
Właściwie mam jutro spotkanie z zespołem C #; Wspomnę im o tym. Przepraszamy za błąd!
źródło
d
może być typu z przeciążonymtrue
operatorem. Wysłałem odpowiedź z przykładem, w którym żadna gałąź nie jest zajęta.Zmienna może zostać nieprzypisana, jeśli wartość wyrażenia dynamicznego jest typu z przeciążonym
true
operatorem .||
Operator będzie wywołaćtrue
operatora zdecydować, czy do oceny prawą stronę, a następnieif
oświadczenie będzie wywołaćtrue
operatora do decydowania o ocenę jego ciało. W przypadku normalnych wynikówbool
zawsze będą one zwracać ten sam wynik, więc dokładnie jeden zostanie oceniony, ale w przypadku operatora zdefiniowanego przez użytkownika nie ma takiej gwarancji!Opierając się na reprodukcji Erica Lipperta, oto krótki i kompletny program, który demonstruje przypadek, w którym żadna ścieżka nie zostanie wykonana, a zmienna będzie miała swoją początkową wartość:
źródło
d
oceniać dwukrotnie? (Nie zaprzeczam, że tak jest , jak pokazałeś). Spodziewałbym się, że oszacowany wyniktrue
(od pierwszego wywołania operatora, przyczyna by||
) zostanie „przekazany” doif
instrukcji. Z pewnością tak by się stało, gdybyś na przykład umieścił tam wywołanie funkcji.d
jest oceniane tylko raz, zgodnie z oczekiwaniami. Jest totrue
operator wywoływany dwukrotnie, raz||
i razif
.var cond = d || M(out y); if (cond) { ... }
. Najpierw oceniamy,d
aby uzyskaćEvilBool
odniesienie do obiektu. Aby ocenić||
, najpierw wywołujemyEvilBool.true
z tym odniesieniem. To zwraca prawdę, więc dokonujemy zwarcia i nie wywołujemyM
, a następnie przypisujemy odwołanie docond
. Następnie przechodzimy doif
oświadczenia.if
Sprawozdanie ocenia swój stan poprzez wywołanieEvilBool.true
.Z MSDN (wyróżnienie moje):
Ponieważ kompilator nie sprawdza typów ani nie rozwiązuje żadnych operacji, które zawierają wyrażenia typu dynamic, nie może zagwarantować, że zmienna zostanie przypisana za pomocą
TryParse()
.źródło
numberGroups
jest przypisywany (wif true
bloku), jeśli nie, drugi warunek gwarantuje przypisanie (przezout
).myString == null
(polegając tylkoTryParse
).if
wyrażenie) dotyczydynamic
zmiennej, nie jest ona rozwiązywana w czasie kompilacji (kompilator nie może zatem przyjąć takich założeń).