Jak poradzić sobie z przeciążeniami metod, gdy argumenty są tego samego typu, ale pobierają inną informację

Odpowiedz Nowy wątek
2019-07-08 17:14
0

Witam,

chciałem się dowiedzieć jak najlepiej rozwiązać taki dylemat.

Mamy taką przykładową metodę:


public MyItem GetSomething(int uniqueID, int page = 1, int pageSize = 10) {
// Zrób coś
return myItem;
}
public MyItem GetSomething(int commonID, int page = 1, int pageSize = 10) {
// Zrób coś podobnego
return myItem;
}

Zastosowanie przeciążenia w tym wypadku z takimi samymi typami argumentów nie jest możliwe, ale zastosowanie dwóch metod o różnych nazwach z kolei jest nieestetyczne, bo metoda zwraca coś z API, którego struktura ma jedną metodę, a ta może pobrać oba argumenty.

W tym wypadku dokładnie odzwierciedlająca strukturę API metoda miałaby taki wygląd:

public MyItem GetSomething(in uniqueID, int commonID, int page = 1, int pageSize = 10) {
// Zrób coś podobnego
return myItem;
}

Niby spoko, ale wymusza to na użytkowniku podanie obu parametrów, gdzie priorytet ma commonID i to na jego podstawie wyświetli się wynik. O ile API nie potrzebuje obu, o tyle metoda w C# już w tym wypadku tak.

Kolejna opcja jaka przyszła mi do głowy:

public MyItem GetSomething(int? uniqueID, int? commonID = null, int page = 1, int pageSize = 10) {
// Zrób coś podobnego
return myItem;
}

W powyższym wypadku null zostanie pominięty, a konieczne jest podanie jednego z argumentów. Tutaj jednak z kolei możliwe jest podanie obu null, więc techniczne też może to wprowadzać błąd i co gorsza trzeba w środku to gdzieś sprawdzić.

Czy jest jakiś magiczny myk, który pozwoliłby estetyczne wymusić na użytkowniku podanie poprawnego parametru uniqueID lub commonID, koniecznie jednego z nich, ale nie pozwalał na obie wartości null? Technicznie podanie obu poprawnych wartości też z punktu widzenia konstrukcji metody API jest dopuszczalne. Jak w takiej sytuacji postępować?

edytowany 2x, ostatnio: Wojciech Dudek, 2019-07-08 17:26

Pozostało 580 znaków

2019-07-08 17:32
2

Teoretycznie mógłbyś jeszcze zastosować coś w stylu specification pattern. Czyli opakować te wszystkie wartości w jakąś klasę,

Pozostało 580 znaków

2019-07-08 17:57
var
3

Możesz jako parametru metody użyć obiektu, w którym na etapie konstruowania sprawdzisz czy przynajmniej jeden z identyfikatorów jest ustawiony, ale ja bym tego specyfikacją nie nazywał

edytowany 1x, ostatnio: var, 2019-07-08 18:00

Pozostało 580 znaków

2019-07-08 18:21
0

Sam tytuł sugeruje nieznajomość zasad języka. Po co się głowić nad czymś czego się nie da zrobić ?

Pozostało 580 znaków

2019-07-08 18:25
0

Nie głowię się nad tym i język znam. Powyższy post sugeruje niezrozumienie treści posta pierwszego. Sugerowanie się samym tytułem jest deczko żałosne bez zapoznania się z treścią posta ze zrozumieniem. Podałem tylko przykład by zobrazować o co mi dokładnie chodzi. Szukam po prostu estetycznego rozwiązania, czegoś co pozwoliłoby w ładny i wygodny sposób dobierać rodzaj ID do zapytania.

edytowany 2x, ostatnio: Wojciech Dudek, 2019-07-08 18:27
Po primo . Ja w ogóle nie rozumiem o co ci chodziło na podstawie tego tytułu. - Zimny Krawiec 2019-07-08 19:26
Rozumiem Cię, bywa. Ja nie wiem o czym jest saga Pieśń Lodu i Ognia zanim przeczytam, ale nie zakładam, że o pieśniach na temat ognia o lodu, bo może się okazać, że jest o świecie fantasy, w którym żywe trupy zagrażają niestabilnemu politycznie królestwu. Nie wiedziałem jak ująć temat, uznałem, że wytłumaczenie na przykładzie przeciążeń metod będzie na tyle klarowne by zrozumieć co mi chodzi po głowie i w czym tkwi mój dylemat, co nie zmienia faktu, że mnie kompletnie nie interesuje wymuszenie czegoś czego zrobić się nie da. - Wojciech Dudek 2019-07-08 20:00
Kompletnie ci się nie udał ten tytuł. Wyglądał jak napisany przez osobę , która nie zna się na programowaniu . Mniejsza o to . Jakbyś wszystko wiedział to byś się nie pytał. - Zimny Krawiec 2019-07-08 20:02
Właśnie i dziękuję za wyrozumiałość. :) - Wojciech Dudek 2019-07-08 20:07
Kolego @Zimny Krawiec jak się nie masz czego czepiać, to czep się tramwaju. Problem jest zasadny i zrozumiały. - Sarrus 2019-07-08 20:47
OK. Przepraszam jeśli kogoś uraziłem . - Zimny Krawiec 2019-07-09 12:01

Pozostało 580 znaków

2019-07-08 18:28
1

A dlaczego nie zastosujesz po prostu Get..Smoething..By..SomethingElse? Przynajmniej wiadomo od razu o co chodzi a nie jakieś podchody.


Np dlatego, żeby nie mieć 10 metod, które wszystkie pod spodem wywołują to samo z innym zestawem parametrów - var 2019-07-08 18:33
10 metod wywołujących jedną nie jest takie złe, jeśli dzięki nim kod ich używający jest prostszy i czytelniejszy :) - mad_penguin 2019-07-08 18:35

Pozostało 580 znaków

2019-07-08 18:30
0

Ehh pisałem o tym. Mogę dodać dwie, a nawet 10 metod, które odwołują się do jakiejś prywatnej wspólnej i tam realizują założenia, jeśli zajdzie potrzeba, pytanie tylko, czy nie dałoby się prościej? Jest konkretne API z dokumentacją, jest jedna metoda. Na razie odpowiedź @var i @szydlak wydaje się całkiem spoko, a na to pytanie które zadałeś powyżej jest odpowiedź w pierwszym poście.

Widzisz wyobraź sobie, że otwierasz dokumentację API, widzisz metodę i chcesz ją wykorzystać, więc wywołujesz sobie klasę klienta i szukasz. O jest! Wspaniale! Ale zaraz, przecież ja chcę użyć commonID, a tu podaje się tylko uniqueID i co teraz? Zaczynasz szukać, o jest ByCommonID fajnie. Jest ok, tylko zastanawia mnie czy nie można tego uprościć, zrobić lepiej, fajniej, sprytniej. Ot cała idea wątku.

Na razie na podstawie odpowiedzi @szydlak i @var za co dziękuję:

public class APIDs
    {
        public int? UniqueID { get; set; }
        public int? CommonID { get; set; }
        public string textID{ get; set; }

        public bool IsValid()
        {
            if (UniqueID != null) return true;
            if (CommonID != null) return true;
            if (textID!= null) return true;

            return false;
        }
    }
edytowany 12x, ostatnio: Wojciech Dudek, 2019-07-08 18:49

Pozostało 580 znaków

2019-07-08 18:49
1

Może taki mały overengineering? Tworzenie osobnych klas dla różnych kryteriów gwarantuje, że nie zostaną podane parametry, których połączenie nie ma sensu i nie będzie możliwości, że jeden nadpisuje drugi itp. co może być mylące.

abstract class Criteria
{
    public int Page { get; set; }
    public int PageSite { get; set; }

    internal abstract MyItem NieWiemJakToNazwać(Func<int?, int?, int, int, MyItem> getSomethingInternal);
}

sealed class CommonCriteria : Criteria
{
    public int CommonId { get; set; }

    internal MyItem NieWiemJakToNazwać(Func<int?, int?, int, int, MyItem> getSomethingInternal)
    {
        return getSomethingInternal(null, CommonId, Page, PageSite);
    }
}

sealed class UniqueCriteria : Criteria
{
    public int UniqueId { get; set; }

    internal MyItem NieWiemJakToNazwać(Func<int?, int?, int, int, MyItem> getSomethingInternal)
    {
        return getSomethingInternal(UniqueId, null, Page, PageSite);
    }
}

public MyItem GetSomething(Criteria criteria)
{
    return criteria.NieWiemJakToNazwać(GetSomethingInternal);
}

private MyItem GetSomethingInternal(in uniqueID, int commonID, int page = 1, int pageSize = 10)
{
    // właściwa implementacja
}
edytowany 1x, ostatnio: mad_penguin, 2019-07-08 18:52

Pozostało 580 znaków

2019-07-08 19:22
0

Bardzo dziękuję za odpowiedzi. Poeksperymentuję sobie i zobaczę co mi bardziej leży.

Pozostało 580 znaków

2019-07-08 21:02
1
var napisał(a):

Możesz jako parametru metody użyć obiektu, w którym na etapie konstruowania sprawdzisz czy przynajmniej jeden z identyfikatorów jest ustawiony, ale ja bym tego specyfikacją nie nazywał

Ja bym się przychylał do czegoś takiego właśnie. Tak przykładowo:

public class ApiId
{
   private int? _uniqueId;
   private int? _commonId;
   public int UniqueId => _uniqueId ?? throw new Exception(...);
   public int CommonId => _commonId ?? throw new Exception(...);
   public bool IsUniqueIdSet => _uniqueId.HasValue;
   public bool IsCommonIdSet => _commonId.HasValue;

   public ApiId(int? uniqueId, int? commonId)
   {
      _uniqueId = uniqueId;
      _commonId = commonId;
   }
}

A funkcja by wyglądała jakoś tak:

public MyResult MyApiFunction(ApiId id)
{
    if (id.IsUniqueIdSet)
       return MyApiFunctionUniqueId(id.UniqueId);
   if (id.IsCommonIdSet)
      return MyApiFunctionCommonId(id.CommonId);

   throw new Exception(...);
}

private MyResult MyApiFunctionUniqueId(int uniqueId)
{
....
}

private MyResult MyApiFunctionCommonId(int commonId)
{
....
}

Zaznaczam, że to jest jedno z setki możliwych podejść. Pytanie na czy możesz zamiennie pobrać informację mając unique id i common id.
Wiele też zależy od skali problemu, bo gdyby takich różnych identyfikatorów (lub innych danych) po których byś pobierał byłoby więcej to można się pokusić o coś w rodzaju tego co podrzucił @mad_penguin.

EDIT:
Można jeszcze prościej po prostu tworząc osobne struktury dla tych identyfikatorów:

public class UniqueId
{
   public int Value {get; set;}
   public UniqueId(int value) => Value = value;
   public static implicit operator int(UniqueId id) => id.Value;
   public static explicit operator UniqueId(int value) => new UniqueId(value); 
}

Wtedy będziesz mógł mieć swoje przeciążenia metod ;)

edytowany 3x, ostatnio: Sarrus, 2019-07-08 21:13

Pozostało 580 znaków

2019-07-08 21:42
0

Wszystko zależy tak na prawdę od tego co się dzieje tu:

public MyItem GetSomething(int uniqueID, int page = 1, int pageSize = 10) {
// Zrób coś
return myItem;
}

Bo jeśli tu jest jakiś klient który woła Api w zależności od parametru można jeszcze inaczej

edytowany 1x, ostatnio: szydlak, 2019-07-08 21:44

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

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