Jak mogę to zrobić elegancko w C # i .NET 3.5 / 4?
Na przykład liczba może wynosić od 1 do 100.
Wiem, że wystarczyłoby proste; ale słowem kluczowym w tym pytaniu jest elegancja. To dla mojego projektu zabawki, a nie do produkcji.
To pytanie nie dotyczyło szybkości, ale piękna kodu. Przestań mówić o wydajności i tym podobnych; pamiętaj, że głosisz w chórze.
if
nie jest „barokowy”, nie naprawiaj tego.Odpowiedzi:
Istnieje wiele opcji:
Sprawdź również ten post SO dla opcji regex.
źródło
if
oświadczenia. To z pewnością powoduje, że ...;)Czy masz na myśli?
lub
źródło
Aby dodać do szumu tutaj, możesz utworzyć metodę rozszerzenia:
Co pozwoliłoby ci zrobić coś takiego ...
Biorąc to pod uwagę, wydaje się to głupią rzeczą do zrobienia, gdy sam czek jest tylko jedną linią.
źródło
Jak powiedzieli inni, użyj prostego if.
Powinieneś pomyśleć o zamówieniu.
na przykład
jest łatwiejszy do odczytania niż
źródło
1 <= x <= 100
.=
zamiast==
. Nie pomaga to w przypadku operatorów relacji opartych na nierówności - ale łatwo jest przyzwyczaić się do konsekwentnego korzystania z niego.x
to złożone wywołanie funkcji lub czasochłonne wyrażenie Linq. W takim przypadku zrobiłbyś to dwa razy, co nie jest dobre. Oczywiście, powinieneś przechowywać tę wartość w tymczasowej zmiennej lokalnej, ale są pewne przypadki (np. W instrukcjach else-if), w których chcesz wywoływać funkcje tylko po innych funkcjach if lub else-if. W przypadku zmiennych tymczasowych i tak musisz je wcześniej wywoływać. W takich przypadkach najlepszym rozwiązaniem jest metoda rozszerzenia (wspomniana w innych odpowiedziach).W kodzie produkcyjnym po prostu napisałbym
Jest to łatwe do zrozumienia i bardzo czytelne.
Oto sprytna metoda, która zmniejsza liczbę porównań z dwóch do jednego dzięki zastosowaniu matematyki. Chodzi o to, że jeden z dwóch czynników staje się ujemny, jeśli liczba leży poza zakresem, a zero, jeśli liczba jest równa jednej z granic:
Jeśli granice są włącznie:
lub
Jeśli granice są wyłączne:
lub
źródło
1 < x && x < 100
w produktywnym kodzie. Łatwiej to zrozumieć.1 < x & x < 100
(no && short circuit) instruuje kompilator, że zawsze może ocenićx < 100
bez względu na wynik1 < x
. O dziwo (ze względu na przewidywanie gałęzi) zawsze szybciej wykonuje się tę prostą operację, niż czasami ją pomija.Proponuję to:
Przykłady:
i oczywiście ze zmiennymi:
Jest łatwy do odczytania (zbliżony do ludzkiego języka) i działa z każdym porównywalnym typem (liczby całkowite, podwójne, typy niestandardowe ...).
Posiadanie łatwego do odczytania kodu jest ważne, ponieważ programista nie marnuje „cykli mózgowych”, aby go zrozumieć. Podczas długich sesji kodowania zmarnowane cykle mózgowe powodują, że programista jest wcześniej zmęczony i podatny na błędy.
źródło
IsInRange. I'm not that keen on Ben's inclusive boolean as that requires a few more brain cycles. It has the advantage that it can be used in any class that that implements IComparer. This is in my Extensions now along with
LiesWithin /LiesInside. Just can't decide which.
NotOutside, ale nie lubię negatywnych warunkówPrzy odrobinie nadużycia metody rozszerzenia możemy uzyskać następujące „eleganckie” rozwiązanie:
źródło
enum Inclusive
z wartości:Lower
,Upper
,All
. I przekazać doIn
funkcji jednym dodatkowym parametrem typuenum Inclusive
o wartości domyślnejInclusive.All
, zaktualizujTo
ciało funkcji do uchwytuAll
,Lower
,Upper
wartości :)Jeśli to przypadek, wystarczy prosty
if
. Jeśli zdarza się to w wielu miejscach, możesz rozważyć te dwa:Coś jak:
źródło
źródło
Użycie
&&
wyrażenia do połączenia dwóch porównań jest po prostu najelegantszym sposobem na zrobienie tego. Jeśli spróbujesz użyć wymyślnych metod rozszerzania i tym podobnych, napotkasz pytanie, czy uwzględnić górną granicę, dolną granicę, czy obie. Gdy zaczniesz dodawać dodatkowe zmienne lub zmieniasz nazwy rozszerzeń, aby wskazać, co jest zawarte, kod staje się dłuższy i trudniejszy do odczytania (dla większości programistów). Ponadto narzędzia takie jak Resharper ostrzegają cię, jeśli twoje porównanie nie ma sensu (number > 100 && number < 1
), czego nie zrobią, jeśli użyjesz metody (`` i.IsBetween (100, 1) '').Jedynym innym komentarzem, jaki mógłbym zrobić, jest to, że jeśli sprawdzasz dane wejściowe z zamiarem zgłoszenia wyjątku, powinieneś rozważyć użycie kontraktów kodu:
Jest to bardziej eleganckie niż
if(...) throw new Exception(...)
i możesz nawet otrzymać ostrzeżenia w czasie kompilacji, jeśli ktoś spróbuje wywołać Twoją metodę bez upewnienia się, że numer znajduje się w granicach.źródło
Jeśli chcesz napisać więcej kodu niż proste if, być może możesz: Utworzyć metodę rozszerzenia o nazwie IsBetween
...
Uzupełnienie:Warto zauważyć, że w praktyce bardzo rzadko „po prostu sprawdzasz równość” (lub <,>) w bazie kodu. (Poza najbardziej trywialnymi sytuacjami.) Jako przykład, każdy programista gier użyłby w każdym projekcie kategorii podobnych do poniższych. Zauważ, że w tym przykładzie jest to (tak się składa), że używa funkcji (Mathf.Approxately), która jest wbudowana w to środowisko; w praktyce zazwyczaj musisz starannie opracować własne koncepcje tego, co oznaczają porównania dla komputerowych reprezentacji liczb rzeczywistych, w zależności od sytuacji, w której się znajdujesz. (Nie wspominaj nawet o tym, że jeśli robisz coś w rodzaju regulatora, regulatora PID lub podobnego, cała sprawa staje się centralna i bardzo trudna, staje się naturą projektu.
źródło
return (value >= Min && value <= Max);
Ponieważ wszystkie inne odpowiedzi nie są wymyślone przeze mnie, tutaj tylko moja implementacja:
Następnie możesz go używać w następujący sposób:
źródło
EDYCJA: Podano nową odpowiedź. Zacząłem używać C #, kiedy napisałem pierwszą odpowiedź na to pytanie, iz perspektywy czasu zdaję sobie sprawę, że moje „rozwiązanie” było / jest naiwne i nieefektywne.
Moja pierwotna odpowiedź: wybrałbym prostszą wersję:
if(Enumerable.Range(1,100).Contains(intInQuestion)) { ...DoStuff; }
Lepszy sposób
Ponieważ nie widziałem innego rozwiązania, które byłoby bardziej wydajne (przynajmniej według moich testów), spróbuję jeszcze raz.
Nowy i lepszy sposób, który działa również z zakresami ujemnymi :
Można tego używać zarówno z dodatnimi, jak i ujemnymi zakresami, a domyślnie jest to zakres
1..100 (włącznie) i używa
x
jako liczby do sprawdzenia, po której następuje opcjonalny zakres zdefiniowany przezmin
imax
.Dodawanie przykładów dobrej miary
Przykład 1:
Zwroty:
Przykład 2: użycie listy losowych liczb całkowitych od 1 do 150
Zwroty:
Czas wykonania: 0,016 sekund (s)
źródło
Nowa wersja starego ulubionego:
źródło
W C, jeśli efektywność czasowa jest kluczowa i przepełnienia całkowite zawiną, można to zrobić
if ((unsigned)(value-min) <= (max-min)) ...
. Jeśli „max” i „min” są zmiennymi niezależnymi, dodatkowe odejmowanie dla (max-min) będzie stratą czasu, ale jeśli to wyrażenie można wstępnie obliczyć w czasie kompilacji lub jeśli można je obliczyć raz w czasie wykonywania, aby przetestować wiele liczb z tego samego zakresu, powyższe wyrażenie można obliczyć wydajnie nawet w przypadku, gdy wartość mieści się w zakresie (jeśli duża część wartości będzie poniżej prawidłowego zakresu, może być szybsze w użyciuif ((value >= min) && (value <= max)) ...
ponieważ zakończy się wcześniej, jeśli wartość jest mniejsza niż min).Zanim jednak skorzystasz z takiej implementacji, wykonaj test porównawczy swojej maszyny docelowej. Na niektórych procesorach wyrażenie dwuczęściowe może być szybsze we wszystkich przypadkach, ponieważ dwa porównania można wykonać niezależnie, podczas gdy w metodzie odejmowania i porównywania odejmowanie musi zostać zakończone, zanim będzie można wykonać porównanie.
źródło
A może coś takiego?
z następującą metodą rozszerzenia (przetestowano):
źródło
Zrobiłbym obiekt Range, coś takiego:
Następnie używasz tego w ten sposób:
W ten sposób możesz użyć go ponownie do innego typu.
źródło
Range
obiekt musi korzystać zCompareTo
metody porównywania elementów, a nie<
operatora.IComparable
i nie przeciążać<
operatora.Sprawdzając, czy „liczba” mieści się w zakresie, musisz jasno określić, co masz na myśli, a co oznacza, że dwie liczby są równe? Ogólnie rzecz biorąc, powinieneś zawijać wszystkie liczby zmiennoprzecinkowe w tak zwaną „kulkę epsilon”. Robi się to przez wybranie małej wartości i stwierdzenie, że jeśli dwie wartości są tak blisko, są tym samym.
Z tymi dwoma pomocnikami na miejscu i założeniem, że jeśli jakakolwiek liczba może zostać rzucona jako podwójna bez wymaganej dokładności. Teraz będziesz potrzebować tylko wyliczenia i innej metody
Druga metoda jest następująca:
To może być o wiele więcej niż to, czego chciałeś, ale powstrzymuje Cię to przed ciągłym zaokrąglaniem i próbą zapamiętania, czy wartość została zaokrąglona i do jakiego miejsca. Jeśli potrzebujesz, możesz łatwo rozszerzyć to, aby działało z dowolnym epsilonem i pozwolić na zmianę epsilon.
źródło
Stosowanie
double numberToBeChecked = 7;
var result = numberToBeChecked.IsBetween (100,122);
var wynik = 5.IsBetween (100,120);
var wynik = 8.0.IsBetween (1.2,9.6);
źródło
Jeśli obawiasz się komentarza @Daap dotyczącego zaakceptowanej odpowiedzi i możesz przekazać wartość tylko raz, możesz wypróbować jedną z następujących czynności
lub
źródło
Jeśli chodzi o elegancję, to, co najbliższe zapisowi matematycznemu ( a <= x <= b ), nieznacznie poprawia czytelność:
Dla dalszych ilustracji:
źródło
Szukałem eleganckiego sposobu na zrobienie tego, w którym granice mogłyby zostać przełączone (tj. Nie byłem pewien, w jakiej kolejności znajdują się wartości).
Będzie to działać tylko w nowszych wersjach języka C #, w których istnieje znak?:
Oczywiście możesz zmienić tam znaki = do swoich celów. Można też polubić rzucanie typów. Potrzebowałem tylko powrotu zmiennoprzecinkowego w granicach (lub równych)
źródło
Elegancki, ponieważ nie wymaga określenia, która z dwóch wartości granicznych jest najpierw większa. Nie zawiera również gałęzi.
źródło
Nie wiem, ale używam tej metody:
I tak mogę to wykorzystać:
źródło
Oto kilka metod rozszerzeń, które mogą pomóc
źródło
Jeśli ma na celu zweryfikowanie parametrów metody, żadne z rozwiązań nie zgłasza ArgumentOutOfRangeException i nie umożliwia łatwej / prawidłowej konfiguracji włączających / wyłącznych wartości min / max.
Użyj w ten sposób
Właśnie napisałem te piękne funkcje. Ma również tę zaletę, że nie ma rozgałęzień (pojedynczego if) dla prawidłowych wartości. Najtrudniejsze jest stworzenie odpowiednich komunikatów o wyjątkach.
źródło
Szukasz
in [1..100]
? To tylko Pascal.źródło
when (number) { in 0..9 -> println("1 digit") in 10..99 -> println("2 digits") in 100..999 -> println("3 digits") }