Czy lepiej pisac try w catch?

0

Czy lepiej pisać try w catch czy tez każdy try oddzielnie?

Przyklad:

var tmp = null; 
try {
  tmp = SzyfrowanieSzybkie
}
catch {}

if (tmp == null) {
  try {
    tmp = SzyfrowanieWolne
  }
  catch {}
}

czy tez:

var tmp = null; 
try {
  tmp = SzyfrowanieSzybkie
}
catch 
{
      try {
        tmp = SzyfrowanieWolne
      }
      catch {}
}

Pomijam to, ze kod SzyfrowanieSzybkie w try i SzyfrowanieWolne w try juz wczesniej ktos napisal. Nie chce robic glebokiego refactoringu i testow.
Jezyk programowania C#/.NET Standard.

2

Jeden z najgorszych przykładów. Skoro jedno szyfrowanie się nie udało, to po co kolejne?

W takim przypadku najlepiej w ogóle tu nie pisać try catch. Metoda szyfrowania mogłaby zwracać rezultat, od którego zależy dalszy flow.

7

Ogólnie to tak jak pisze @SkrzydlatyWąż - świat powoli przechodzi na Eithery albo chociaż Option/Optional. Jak twój język wspiera generyki to jest to IMHO lepsze rozwiązanie. Wyjątki powinny być do rzeczy naprawde wyjątkowych, albo takich co nie da się ich w ogóle naprawić i można tylko wtedy zwrócić błąd. W np w takich Rust i Haskell to rzucenie wyjątku, (a w zasadzie errora) ubija wąteki oznacza dużą rozpierduchę XD

Update Jakby szyfrowanieSzybkie i szyfrowanieWolne zwracało Either<Error, Result> to można napisać w pseudokodzie:

szyfrowanieSzybkie(text).lefFlattMap(error => szyfrowanieWolne(text))

W zasadzie to z takiego kodu jeszcze nie jestem zadowoly, ale to dobry początek :P

0
still.still napisał(a):

Czy lepiej pisać try w catch czy tez każdy try oddzielnie?

Jeśli chcesz coś zrobić, tylko wtedy kiedy się czegoś nie udało zrobić to lepiej try w innym catch. Po to jest. Ten if to overkill.

KamilAdam napisał(a):

Ogólnie to tak jak pisze @SkrzydlatyWąż - świat powoli przechodzi na Eithery albo chociaż Option/Optional. Jak twój język wspiera generyki to jest to IMHO lepsze rozwiązanie. Wyjątki powinny być do rzeczy naprawde wyjątkowych, albo takich co nie da się ich w ogóle naprawić i można tylko wtedy zwrócić błąd. W np w takich Rust i Haskell to rzucenie wyjątku, (a w zasadzie errora) ubija wąteki oznacza dużą rozpierduchę XD

To czy wyjątki są nadal zasadne to trochę inny temat, nie wiem czy warto rozpoczynać rozmowę na ten temat w tym wątku.

SkrzydlatyWąż napisał(a):

Jeden z najgorszych przykładów. Skoro jedno szyfrowanie się nie udało, to po co kolejne?

W takim przypadku najlepiej w ogóle tu nie pisać try catch. Metoda szyfrowania mogłaby zwracać rezultat, od którego zależy dalszy flow.

Gość pyta o to, czy lepiej odpalić kod if czy catch, nie wiesz co chce napisać, więć nie kwestionuj od początku, bo nie wiesz co OP w ogóle próbuje zrobić.

0

Ale przecież te 2 fragmenty robią coś innego, chyba że źle widzę albo w linii 7 pierwszego fragmentu miało być == zamiast != ?
Bo rozumiem, że chodzi o przypadek, że mamy 2 funkcje, i jeśli jedna nie zadziała (np. mamy procesor bez wsparcia SzybkieSzyfrowanie), to próbuj drugą?

1
KamilAdam napisał(a):

Ogólnie to tak jak pisze @SkrzydlatyWąż - świat powoli przechodzi na Eithery albo chociaż Option/Optional. Jak twój język wspiera generyki to jest to IMHO lepsze rozwiązanie. Wyjątki powinny być do rzeczy naprawde wyjątkowych, albo takich co nie da się ich w ogóle naprawić i można tylko wtedy zwrócić błąd. W np w takich Rust i Haskell to rzucenie wyjątku, (a w zasadzie errora) ubija wąteki oznacza dużą rozpierduchę XD

Update Jakby szyfrowanieSzybkie i szyfrowanieWolne zwracało Either<Error, Result> to można napisać w pseudokodzie:

szyfrowanieSzybkie(text).lefFlattMap(error => szyfrowanieWolne(text))

W zasadzie to z takiego kodu jeszcze nie jestem zadowoly, ale to dobry początek :P

Zakładając, że te szyfrowania da się zapakować w interfejs funkcyjny Function<InputData, Optional<EncryptedData>> , to można by text leniwie przepuścić przez strumień tych szyfrowań (w javie, w .NET, to nie wiem ;) )

Stream.of(Encryption::Fast, Encryption::Slow, Encryption::Dummy)
      .map(func -> func.apply(plainData))
      .filter(Optional::isPresent)
      .findFirst()
      .orElse(Optional.empty())
;
1
yarel napisał(a):
Stream.of(Encryption::Fast, Encryption::Slow, Encryption::Dummy)
      .map(func -> func.apply(plainData))
      .filter(Optional::isPresent)
      .findFirst()
      .orElse(Optional.empty())
;

I móglbyś podać jakąś faktyczną wadę albo zaletę takiej implementacji nad tą z try catchami?

Bo jak widzę to podałeś alternatywną implementację, ale nie widzę żeby ona była faktycznie jakoś lepsza albo gorsza?

2
SkrzydlatyWąż napisał(a):

Jeden z najgorszych przykładów. Skoro jedno szyfrowanie się nie udało, to po co kolejne?

W takim przypadku najlepiej w ogóle tu nie pisać try catch. Metoda szyfrowania mogłaby zwracać rezultat, od którego zależy dalszy flow.

OP zadał bardzo proste pytanie. Ma do dyspozycji kilka procedur, z które nie są w pełni niezawodne i chce je uruchamiać w określonej kolejności do uruchomienia pierwszej wykonanej bezbłędnie. W najgorszym to będą wszystkie procedury. Generalnie na zasadzie "uruchamiam pierwszy algorytm, jak będzie wyjątek, to uruchamiam drugi, jeżeli drugi rzuci wyjątek, to uruchamiam trzeci".

W tym przykładzie, co oczywiste, jak algorytm rzuci wyjątek, to nie zmieni zmiennej. Oba zaprezentowane kawałki kodu są równoważne, przy czym zasadność uruchamiania kolejnego algorytmu jest badana albo przez sprawdzenie, czy wynikowa zmienna dostała wartość lub poprzez wywołanie w bloku "catch".

1
Riddle napisał(a):
yarel napisał(a):
Stream.of(Encryption::Fast, Encryption::Slow, Encryption::Dummy)
      .map(func -> func.apply(plainData))
      .filter(Optional::isPresent)
      .findFirst()
      .orElse(Optional.empty())
;

I móglbyś podać jakąś faktyczną wadę albo zaletę takiej implementacji nad tą z try catchami?

Bo jak widzę to podałeś alternatywną implementację, ale nie widzę żeby ona była faktycznie jakoś lepsza albo gorsza?

Bez szerszego kontekstu raczej ciężko ocenić czy lepsza / gorsza / porównywalna.

Widzę takie plusy:

  • krótsza (szybciej się czyta, łatwiej wyłapać błędy)
  • rzucanie i obsługa wyjątków jest kosztowna (trzeba stworzyć obiekt wyjątku, zebrać stack trejsa, przetworzyć go i zlokalizować najbliższego kandydata na przetworzenie wyjątku itd.)
  • przy 3-4 algorytmach nadal będzie krótsza, zaś przy try-catch i powyższych propozycjach zostaną one zaorane, albo wjedzie copy-paste-pattern
  • bardzo prosto ten strumień wyciągnąć na zewnątrz i zostawić funkcjonalność, która będzie spełniała zasadę OCP (ze względu na nowe algorytmy szyfrowania); przy OOP też się da, aczkolwiek będzie więcej kodu
7
yarel napisał(a):
Stream.of(Encryption::Fast, Encryption::Slow, Encryption::Dummy)
      .map(func -> func.apply(plainData))
      .filter(Optional::isPresent)
      .findFirst()
      .orElse(Optional.empty())
;

O ile się nie mylę to mogłeś to napisać tak:

Encryption.Fast(plainData).orElseGet(()-> Encryption.Slow(plainData))
0

No to zalezy od specyfikacji. Może chcesz odpalać inne szyfrowanie jak coś się wywali, albo może chcesz wywalić całą aplikacje? To tylko Ty możesz wiedzieć, albo twój TL

0
still.still napisał(a):
var tmp = null; 
try {
  tmp = SzyfrowanieSzybkie
}
catch {}

if (tmp == null) {
  try {
    tmp = SzyfrowanieWolne
  }
  catch {}
}

czy tez:

var tmp = null; 
try {
  tmp = SzyfrowanieSzybkie
}
catch 
{
      try {
        tmp = SzyfrowanieWolne
      }
      catch {}
}

Z dwóch wymienionych zakładając że SzyfrowanieSzybkie rzuca exception zamiast zwracać false / null to wydzielenie do osobnej metody i zwyczajnie:

try {
  return SzyfrowanieSzybkie();
}
catch { // złap odpowiedni wyjątek, pewnie go znasz a szyfrowania nie ma sensu powtarzać np w przypadku OutOfMemory
  return SzyfrowanieWolne();
}
jarekr000000 napisał(a):
yarel napisał(a):
Stream.of(Encryption::Fast, Encryption::Slow, Encryption::Dummy)
      .map(func -> func.apply(plainData))
      .filter(Optional::isPresent)
      .findFirst()
      .orElse(Optional.empty())
;

O ile się nie mylę to mogłeś to napisać tak:

Encryption.Fast(plainData).orElseGet(()-> Encryption.Slow(plainData))

w C# po prostu by się napisało:

Encryption.Fast(plainData) ?? Encryption.Slow(plainData)

Optional w c# za bardzo nie ma sensu, można sobie napisać extensiony do Nullable<> i T? jak ktoś chce.

aLe oPtiOnAl wYrAżA żE wYnIk mOże ByĆ pUsTy

tak samo jak ? na końcu zwracanego typu

0

U nas na kierunku za pustego catcha było bicie po łapach, aczkolwiek dla programistów C# można zrobić wyjątek ze względu na to, że są już wystarczająco pokrzywdzeni przez swoje błędy życiowe.

1 użytkowników online, w tym zalogowanych: 0, gości: 1