Sprawdź ich IL, w ten sposób będziesz miał lepszy pomysł, czy będą produkować ten sam kod, czy też są obsługiwane inaczej
manman
12
@manman Jeśli ktoś nie rozumie kodu wysokiego poziomu, co sprawia wrażenie, że kod IL będzie bardziej czytelny?
Servy
11
@Servy Kiedy niektórzy są bardzo ciekawi różnicy, a wszyscy inni omawiają dyskusje na wysokim szczeblu, dobrym pomysłem jest wskazanie innych sposobów zrozumienia różnicy, a jeśli jest bardzo ciekawy, może pójść i po prostu porównać wynik, nie trzeba przeczytać cały IL
manman
6
Jedyną różnicą jest to, czy oceniasz s.MyNameraz, czy dwa razy.
Tim S.
2
@Servy, ponieważ nie musisz rozumieć kodu IL, po prostu musisz to diffzrobić
Generalnie zwrócą ten sam wynik. Jest jednak kilka przypadków, w których zauważysz zauważalne różnice, gdy MyNamejest to właściwość, ponieważ MyNamemetoda pobierająca zostanie wykonana dwukrotnie w pierwszym przykładzie i tylko raz w drugim przykładzie.
Na przykład mogą wystąpić różnice w wydajności przy MyNamedwukrotnym wykonaniu :
string MyName
{
get
{
Thread.Sleep(10000);
return"HELLO";
}
}
Lub możesz uzyskać inne wyniki, wykonując MyNamedwa razy, jeśli MyNamejest stanowy:
Lub możesz uzyskać inne wyniki po MyNamedwukrotnym wykonaniu, jeśli MyNamemożna to zmienić w innym wątku:
voidChangeMyNameAsync()
{
//MyName set to null in another thread which makes it //possible for the first example to return null
Task.Run(() => this.MyName = null);
}
string MyName { get; set; }
Oto, jak kompilowany jest rzeczywisty kod. Najpierw utwór z potrójnym wyrażeniem:
IL_0007: ldloc.0// s
IL_0008: callvirt s.get_MyName <-- first call
IL_000D: brfalse.s IL_0017
IL_000F: ldloc.0// s
IL_0010: callvirt s.get_MyName <-- second call
IL_0015: br.s IL_001C
IL_0017: ldsfld System.String.Empty
IL_001C: call set_MyName
a oto fragment z operatorem koalescencji zerowej:
IL_0007: ldloc.0// s
IL_0008: callvirt s.get_MyName <-- only call
IL_000D: dup
IL_000E: brtrue.s IL_0016
IL_0010: pop
IL_0011: ldsfld System.String.Empty
IL_0016: call s.set_MyName
Jak widać, skompilowany kod dla operatora trójskładnikowego wykona dwa wywołania w celu uzyskania wartości właściwości, podczas gdy operator łączący wartość null wykona tylko 1.
MyName = s.MyName ?? string.Emptynie wykona metody pobierającej dwukrotnie, jeśli MyNamejest właściwością. Więc jeśli nie chcesz dwukrotnie wykonywać gettera, powinieneś użyć drugiej linii.
Myślę, że przegapiłeś „s” w ostatniej części kodu IL; obecnie czyta et_MyName, co powinno być set_MyName.
AJMansfield,
2
Ponadto długa wersja z ?:może faktycznie skutkować nullz powyższych powodów. To mogłoby być bardziej obrazowe, gdyby "Value from other thread"powyżej był faktycznie nullciągiem. To pokazałoby, że wynik może być zerowy. To samo mogłoby się zdarzyć z obiektem stanowym, gdyby obiekt zdecydował się zwrócić wystąpienie ciągu znaków przy pierwszym wywołaniu getaccessoor i zwrócić wartość null tylko przy drugim wywołaniu.
Jeppe Stig Nielsen
2
Zwróć również uwagę, że specyfikacje języka C # 4.0 zapewniają (w §7.14), że MyValue zostanie rzeczywiście dwukrotnie ocenione, zapobiegając w ten sposób jitterowi optymalizacji tego podwójnego wywołania (tak jak myślałem na początku)
BlackBear
26
Jeśli właściwość jest czymś więcej niż prostym pobieraczem, możesz wykonywać funkcję dwukrotnie w przypadku niezerowym dla pierwszej.
Jeśli właściwość jest obiektem stanowym, drugie wywołanie właściwości może zwrócić inny wynik:
Ponadto w przypadku nie będącym łańcuchem klasa może przeciążać ==, aby zrobić coś innego niż operator trójargumentowy. Nie wierzę, że operator trójskładnikowy może być przeciążony.
Operator trójargumentowy zdecydowanie nie może być przeciążony. Nawet C ++, który pozwala na przeciążenie praktycznie każdego innego operatora, nie ma możliwości przeciążenia ?:. Nie widziałem żadnego języka, który to robi.
Darrel Hoffman
@DarrelHoffman Jest zawsze Scala, w której możesz toczyć własną;)
jdphenix
9
Jedyną różnicą jest to, czy oceniasz s.MyNamedwukrotnie czy raz. Pierwsza zrobi to dwukrotnie w przypadku, gdy s.MyNamenie jest zerowa, druga tylko raz oceni to.
W większości przypadków ta różnica nie ma znaczenia i wybrałbym drugą, ponieważ jest bardziej przejrzysta i zwięzła.
Jeśli używam disemblera, to widzę, że pierwsza instrukcja wymaga 19 instrukcji do wykonania przez kompilator, podczas gdy druga wymaga tylko 12 instrukcji do wykonania.
Dziękuję za podanie nazwiska. Chciałem sprawdzić, czy Java również to ma? operator. I tak nie jest, więc pozostanę przy operatorze trójskładnikowym dla obu.
developerwjk
0
Tak, robią to samo. ??jest skrótem do sprawdzania null.
Nie. Obaj robią to samo. Drugi jest skuteczny. Co zwraca rzeczywistą wartość, jeśli nie jest zerowa. W przeciwnym razie zostanie zwrócona wartość po prawej stronie.
s.MyName
raz, czy dwa razy.diff
zrobićOdpowiedzi:
AKTUALIZACJA: Napisałem post na blogu, który bardziej szczegółowo omawia ten temat. http://www.codeducky.org/properties-fields-and-methods-oh-my/
Generalnie zwrócą ten sam wynik. Jest jednak kilka przypadków, w których zauważysz zauważalne różnice, gdy
MyName
jest to właściwość, ponieważMyName
metoda pobierająca zostanie wykonana dwukrotnie w pierwszym przykładzie i tylko raz w drugim przykładzie.Na przykład mogą wystąpić różnice w wydajności przy
MyName
dwukrotnym wykonaniu :string MyName { get { Thread.Sleep(10000); return "HELLO"; } }
Lub możesz uzyskać inne wyniki, wykonując
MyName
dwa razy, jeśliMyName
jest stanowy:private bool _MyNameHasBeenRead = false; string MyName { get { if(_MyNameHasBeenRead) throw new Exception("Can't read MyName twice"); _MyNameHasBeenRead = true; Thread.Sleep(10000); return "HELLO"; } }
Lub możesz uzyskać inne wyniki po
MyName
dwukrotnym wykonaniu, jeśliMyName
można to zmienić w innym wątku:void ChangeMyNameAsync() { //MyName set to null in another thread which makes it //possible for the first example to return null Task.Run(() => this.MyName = null); } string MyName { get; set; }
Oto, jak kompilowany jest rzeczywisty kod. Najpierw utwór z potrójnym wyrażeniem:
IL_0007: ldloc.0 // s IL_0008: callvirt s.get_MyName <-- first call IL_000D: brfalse.s IL_0017 IL_000F: ldloc.0 // s IL_0010: callvirt s.get_MyName <-- second call IL_0015: br.s IL_001C IL_0017: ldsfld System.String.Empty IL_001C: call set_MyName
a oto fragment z operatorem koalescencji zerowej:
IL_0007: ldloc.0 // s IL_0008: callvirt s.get_MyName <-- only call IL_000D: dup IL_000E: brtrue.s IL_0016 IL_0010: pop IL_0011: ldsfld System.String.Empty IL_0016: call s.set_MyName
Jak widać, skompilowany kod dla operatora trójskładnikowego wykona dwa wywołania w celu uzyskania wartości właściwości, podczas gdy operator łączący wartość null wykona tylko 1.
źródło
MyName = s.MyName ?? string.Empty
nie wykona metody pobierającej dwukrotnie, jeśliMyName
jest właściwością. Więc jeśli nie chcesz dwukrotnie wykonywać gettera, powinieneś użyć drugiej linii.et_MyName
, co powinno byćset_MyName
.?:
może faktycznie skutkowaćnull
z powyższych powodów. To mogłoby być bardziej obrazowe, gdyby"Value from other thread"
powyżej był faktycznienull
ciągiem. To pokazałoby, że wynik może być zerowy. To samo mogłoby się zdarzyć z obiektem stanowym, gdyby obiekt zdecydował się zwrócić wystąpienie ciągu znaków przy pierwszym wywołaniuget
accessoor i zwrócić wartość null tylko przy drugim wywołaniu.Jeśli właściwość jest czymś więcej niż prostym pobieraczem, możesz wykonywać funkcję dwukrotnie w przypadku niezerowym dla pierwszej.
Jeśli właściwość jest obiektem stanowym, drugie wywołanie właściwości może zwrócić inny wynik:
class MyClass { private IEnumerator<string> _next = Next(); public MyClass() { this._next.MoveNext(); } public string MyName { get { var n = this._next.Current; this._next.MoveNext(); return n; } } public static IEnumerator<string> Next() { yield return "foo"; yield return "bar"; } }
Ponadto w przypadku nie będącym łańcuchem klasa może przeciążać ==, aby zrobić coś innego niż operator trójargumentowy. Nie wierzę, że operator trójskładnikowy może być przeciążony.
źródło
?:
. Nie widziałem żadnego języka, który to robi.Jedyną różnicą jest to, czy oceniasz
s.MyName
dwukrotnie czy raz. Pierwsza zrobi to dwukrotnie w przypadku, gdys.MyName
nie jest zerowa, druga tylko raz oceni to.W większości przypadków ta różnica nie ma znaczenia i wybrałbym drugą, ponieważ jest bardziej przejrzysta i zwięzła.
źródło
Tak, oba są takie same i jest to operator łączenia zerowego .
Jeśli mówimy wtedy o wydajności
string MyName = (s.MyName == null) ? string.Empty : s.MyName; string MyName2 = s.MyName ?? string.Empty;
Jeśli używam disemblera, to widzę, że pierwsza instrukcja wymaga 19 instrukcji do wykonania przez kompilator, podczas gdy druga wymaga tylko 12 instrukcji do wykonania.
źródło
Tak, robią to samo.
??
jest skrótem do sprawdzania null.źródło
Wykonują to samo zadanie.
Jedyną różnicą byłaby czytelność, czy Twoi współpracownicy lub ktokolwiek czyta kod, rozumie składnię.
EDYCJA: Dodatkowo pierwsza opcja może
MyName
dwukrotnie ocenić właściwość .źródło
Nie. Obaj robią to samo. Drugi jest skuteczny. Co zwraca rzeczywistą wartość, jeśli nie jest zerowa. W przeciwnym razie zostanie zwrócona wartość po prawej stronie.
Zapoznaj się z tym http://msdn.microsoft.com/en-us/library/ms173224.aspx
Mam nadzieję że to pomoże.
źródło