Chciałbym aby Funkcja zwracała mi różne dane w zależności od przekazanych parametrów. Jeśli wartość paramtru1 będzie 'A' to funkcja ma zwrócić Stringa, jeśli wartość parametru1 będzie 'C' to funckaj zwraca datę itd.
Jest to możliwe do wykonania?
- Rejestracja:ponad 10 lat
- Ostatnio:ponad 8 lat
- Postów:165
- Rejestracja:ponad 10 lat
- Ostatnio:ponad 8 lat
- Postów:165
To raczej nie sprawdzi się u mnie, chyba że coś źle zrozumiałem.
Ja mam zmienne już określone i niejestem w stanie ich zmienić bo bym musiał przebudować cały kod. Chciałem stworzyć uniwersalną funkcję, która by przypisywała wartość w zależności od parametrów. Wszystkie wartości początkowo widnieją jako stringi w tablicy. Po podaniu w parametrze funkcji słowa kluczowego np. "data" wartość funkcji od razu stawałaby się datą, gdy słowo kluczowe będzie "nazwisko" to wtedy wynikiem jest string, a gdy np. słowo kluczowe to "numer" wtedy wynikiem funkcji jest integer.

- Rejestracja:ponad 13 lat
- Ostatnio:około 4 godziny
- Lokalizacja:Tuchów
- Postów:12167
Chciałem stworzyć uniwersalną funkcję, która by przypisywała wartość w zależności od parametrów.
No dobrze, ale jaki typ miałaby zwracać ta funkcja, żeby mogła zwracać dowolne dane? Jedyne co mi do głowy przychodzi to goły Pointer, jednak kod musiałbyś przerobić;
Po podaniu w parametrze funkcji słowa kluczowego np. "data" wartość funkcji od razu stawałaby się datą, gdy słowo kluczowe będzie "nazwisko" to wtedy wynikiem jest string, a gdy np. słowo kluczowe to "numer" wtedy wynikiem funkcji jest integer.
Taki efekt możesz uzyskać za pomocą przeładowywania:
uses
SysUtils;
function Convert(const AValue: String): TDateTime; { overload; }
begin
Result := StrToDate(AValue);
end;
function Convert(const AValue: String): String; { overload; }
begin
Result := AValue;
end;
function Convert(const AValue: String): Integer; { overload; }
begin
Result := StrToInt(AValue);
end;
W Lazarusie nie trzeba zakańczać deklaracji tych funkcji słówkiem overload (dlatego są zaremowane);
I teraz możesz używać teoretycznie jednej funkcji, która zwracać będzie dane różnego typu; Ograniczenie jest takie, że typy danych muszą się w każdej z nich różnić - albo typy parametrów, albo typ zwracany; Dzięki temu możliwe będą poniższe wywołania:
var
dtValue: TDateTime;
strValue: String;
intValue: Integer;
begin
dtValue := Convert('01:03:56');
strValue := Convert('foo bald');
intValue := Convert('12345678');
Możesz też ustalić ten sam typ zwracany (np. String), ale różne typy argumentów.
@dani17: kombinujesz cholernie pod górę.
Jeśli potrzebujesz, aby wywołanie getCostam('a')
zwracało integer
, a getCostam('b')
- ciąg znaków, utwórz odrębne metody getCostamA()
oraz getCostamB()
- to jedyna słuszna oraz poprawna metoda.


- Rejestracja:ponad 10 lat
- Ostatnio:ponad 8 lat
- Postów:165
Patryk27 napisał(a):
@dani17: kombinujesz cholernie pod górę.
Jeśli potrzebujesz, aby wywołaniegetCostam('a')
zwracałointeger
, agetCostam('b')
- ciąg znaków, utwórz odrębne metodygetCostamA()
orazgetCostamB()
- to jedyna słuszna oraz poprawna metoda.
no dobrze, ale to musiałbym takich metod stworzyć kilkadziesiąt. mam 20 róznych słów kluczowych, które mają zwracać String, 20 zwracających integer i kilka zwracających datę. dlatego chciałbym to stworzyć w jednej funkcji i najlepiej napisać if (argument = 'slowo') or (argument = 'inne slowo') then result: string else result: inetger;
to nie jest poprawny zapis, wiem, ale chcę w ten sposób pokazać co chciałbym osiągnąć.
no dobrze, ale to musiałbym takich metod stworzyć kilkadziesiąt.
No to stwórz ich kilkadziesiąt - przecież już dawno temu miałbyś je napisane (zakładając nawet, że nie masz automatycznego generatora getterów/setterów w środowisku) :P
Naprawdę nie ma co robić sobie drogi na skróty - w programowaniu liczy się dokładność. Robiąc coś na odczep zachowujesz sobie jedynie pięć minut dzisiaj, tracąc pięć godzin w przyszłości na szukaniu błędu.

- Rejestracja:ponad 13 lat
- Ostatnio:około 4 godziny
- Lokalizacja:Tuchów
- Postów:12167
dani17 napisał(a)
dlatego chciałbym to stworzyć w jednej funkcji i najlepiej napisać if (argument = 'slowo') or (argument = 'inne slowo') then result: string else result: inetger;
Nie możesz napisać jednej funkcji, która może zwracać trzy różne typy danych - albo String, albo TDate, albo Integer; Musisz określić konkretny typ danych zwracanych przez funkcję; Możesz skorzystać z typu Variant, aby móc zwracać dane różnych typów, jednak musiałbyś zmienić również typy danych w programie; W ogóle to nie ma się nad czym zastanawiać;
no dobrze, ale to musiałbym takich metod stworzyć kilkadziesiąt. mam 20 róznych słów kluczowych, które mają zwracać String, 20 zwracających integer i kilka zwracających datę.
Skoro jest ich tyle to napisz sobie trzy przeładowane (dla każdego typu jedna).

- Rejestracja:ponad 17 lat
- Ostatnio:około 8 godzin
- Postów:1595
dani17 napisał(a):
no dobrze, ale to musiałbym takich metod stworzyć kilkadziesiąt. mam 20 róznych słów kluczowych, które mają zwracać String, 20 zwracających integer i kilka zwracających datę. dlatego chciałbym to stworzyć w jednej funkcji i najlepiej napisać if (argument = 'slowo') or (argument = 'inne slowo') then result: string else result: inetger;
to nie jest poprawny zapis, wiem, ale chcę w ten sposób pokazać co chciałbym osiągnąć.
Nie wiem jak w lazarusie ale w delphi da się to zrobić używając generyków i żeby przyspieszyć TDictionary.
TVirtualFileClass = class
public
class function GetResult: Variant virtual; abstract;
end;
TFileTypeClass = class of TVirtualFileClass;
TFileTypeClassA = class(TVirtualFileClass )
public
class function GetResult: Variant; override;
end;
TFileTypeClassB = class(TVirtualFileClass )
public
class function GetResult: Variant; override;
end;
Każda klasa będzie miała swoją funkcję GetResult i wywołanie będziesz miał tylko:
RegistrationClass(const aClassType: TFileTypeClass);
wewnątrz możesz sobie to dodawać do swojego dicta lub klasy dziedziczącej z dicta np tak:
type
TKBFilesType = class(TDictionary<string, TFileTypeClass>)
to oczywiście tylko przykładowe definicje ale ja mam tak zrobione np wczytywanie plików operuję na jednym typie czyli
TNazwaKlasy = class of TNazwaKlasyVirtualnej
i reszta dzieje się w definicjach poszczególnych klas. Nie ma dzięki temu w kodzie warunków: if ExtractFileExtention(pFilneName) = 'txt' then ... else if ...
- Rejestracja:ponad 10 lat
- Ostatnio:ponad 8 lat
- Postów:165
Patryk27 napisał(a):
no dobrze, ale to musiałbym takich metod stworzyć kilkadziesiąt.
No to stwórz ich kilkadziesiąt - przecież już dawno temu miałbyś je napisane (zakładając nawet, że nie masz automatycznego generatora getterów/setterów w środowisku) :PNaprawdę nie ma co robić sobie drogi na skróty - w programowaniu liczy się dokładność. Robiąc coś na odczep zachowujesz sobie jedynie pięć minut dzisiaj, tracąc pięć godzin w przyszłości na szukaniu błędu.
Tak też zrobię, w końcu wywołanie takiej funkcji będzie niemal identyczne jak to czego chciałem. Jedynie zamiast wpisywać słowo kluczowe jako parametr to będzie się ono znajdowało w nazwie funkcji.
Ale teraz mam inne pytanie. Jeżeli mam zmienną Imie: String;
to czy da się zrobić aby jakaś inna zmienna przyjęła jako wartość nazwę innej zmiennej? I tak samo w drugą stronę, w sumie bardziej mi na tym zależy. Mam Tablica: Array of String = ('Imie', 'Nazwisko', 'Miasto');
i czy teraz da się zrobić coś takiego:
for I := 0 to Length(Tablica) - 1 do
Zmienna{Tablica[I]} := Get{Tablica[I]}
Oczywiście mam zadeklarowane zmienne które nazywają się tak jak wartości tablicy. Czy jednak nie da się tego przyśpieszyć i trzeba robić tak:
for I := 0 to Length(Tablica) - 1 do
begin
if Tablica[I] = 'Imie' then
Imie := GetImie;
if Tablica[I] = 'Nazwisko' then
Nazwisko := GetNazwisko;
if Tablica[I] = 'Miasto' then
Miasto := GetMiasto;
end;
Znowu próbujesz sobie torować drogę na skróty :P
Teoretycznie mógłbyś spróbować utworzyć sobie klasę zawierającą metody GetImie()
, GetNazwisko()
oraz GetMiasto()
(przy okazji: nie wykorzystuj polskiego nazewnictwa w kodzie, nigdy) oraz pobawić się z RTTI, natomiast to by było czysto dla zabawy i bardzo nie chciałbym spotkać czegoś takiego w normalnym, produkcyjnym kodzie.

bo oni się jeszcze uczą
? Albo pozwalać młodym dzieciom kraść? Podobnie tu - od początku należy wpajać dobre nawyki.




od początku należy wpajać dobre nawyki.
-- zgadzam się. Z pisaniem po polsku "bo tylko ja będę korzystał" jest taki problem, że nigdy tego nie wiesz do końca. jeśli ktoś pisze po polsku to zamyka się na możliwość zadania pytania na StackOverflow nawet, bo nikt nie zrozumie jego kodu i nikt nie będzie mógł mu pomóc. Więc jest to bardzo krótkowzroczne i warto jak najszybciej przejść na angielski, nawet jak się stosuje tę karygodną praktykę pisania po polsku.

- Rejestracja:ponad 13 lat
- Ostatnio:około 4 godziny
- Lokalizacja:Tuchów
- Postów:12167
W dalszym ciągu wygląda to na kosmiczne kombinacje; Przy czym zamiast podawać nazwy zmiennych (i trzymać te nazwy w tablicy), możesz skorzystać z typu wyliczeniowego i rozróżnienie wartości zrobić czytelnie w instrukcji wyboru:
type
TFieldKind = (fkName, fkSurname, fkCity);
function Foo(AFieldKind: TFieldKind): String;
begin
case AFieldKind of
fkName: Result := GetName();
fkSurname: Result := GetSurname();
fkCity: Result := GetCity();
end;
end;
I wywołania:
Name := Foo(fkName);
Surname := Foo(fkSurname);
City := Foo(fkCity);
Ale takie cuś raczej nie ma sensu - wygodniej będzie zrobić sobie więcej funkcji, nazwać je sensownie i używać; Przy czym jeśli funkcje mają elementy wspólne to można je pogrupować.
- Rejestracja:ponad 10 lat
- Ostatnio:ponad 8 lat
- Postów:165
Patryk27 napisał(a):
Znowu próbujesz sobie torować drogę na skróty :P
Teoretycznie mógłbyś spróbować utworzyć sobie klasę zawierającą metody
GetImie()
,GetNazwisko()
orazGetMiasto()
(przy okazji: nie wykorzystuj polskiego nazewnictwa w kodzie, nigdy) oraz pobawić się z RTTI, natomiast to by było czysto dla zabawy i bardzo nie chciałbym spotkać czegoś takiego w normalnym, produkcyjnym kodzie.
Nie, nie. Ja mam to i tak napisane inaczej. Tylko byłem ciekawy czy coś takiego może istnieje. Bo jakby była jakaś funkcja która by to umożliwiła to kod byłby na pewno bardziej przejrzysty w tym konkretnym miejscu.
Chciałbym aby Funkcja zwracała mi różne dane w zależności od przekazanych parametrów. Jeśli wartość paramtru1 będzie 'A' to funkcja ma zwrócić Stringa, jeśli wartość parametru1 będzie 'C' to funckaj zwraca datę itd.
Jest to możliwe do wykonania?
W popularnych językach takich jak Java, C++, C#, Delphi nie jest możliwe zakodowanie czegoś takiego w systemie typów.
Jeżeli typ ma zależeć od wartości (a nie od typu wartości), to potrzebujesz języka, który potrafi tzw. "dependent types".
Takie języki to m.in. Agda, Coq, Idris i parę innych mniej znanych. Do pewnego stopnia da się zasymulować typy zależne w językach takich jak Haskell, Scala, Dotty.
- Rejestracja:około 21 lat
- Ostatnio:około 2 miesiące
- Postów:1082
Patryk27 napisał(a):
no dobrze, ale to musiałbym takich metod stworzyć kilkadziesiąt.
No to stwórz ich kilkadziesiąt - przecież już dawno temu miałbyś je napisane (zakładając nawet, że nie masz automatycznego generatora getterów/setterów w środowisku) :PNaprawdę nie ma co robić sobie drogi na skróty - w programowaniu liczy się dokładność. Robiąc coś na odczep zachowujesz sobie jedynie pięć minut dzisiaj, tracąc pięć godzin w przyszłości na szukaniu błędu.
Zgoda. Tylko taka rada to jest właśnie na odczep się. Nie ma nic "fajniejszego" w programowaniu, jak robienie tego samego na kilkadziesiąt różnych sposobów. To był sarkazm, oczywiście...
Pytanie jest o FPC, a ja sie na tym nie znam, zatem...
Ale FPC ma coś takiego jak TValue
? Albo - poprawnie obsługuje typy generyczne?
Tak czy siak; ja mam np. coś takiego:
function AsBusinessObject<T : TBaseBusinessObjectQuery>: T;
A używa się tego tak:
AsBusinessObject<TboPpTask>.Duration := 3.5;
AsBusinessObject<TboPpTextTask>.Duration := 'trzy i pół godziny';
Tak, mógłbym napisać kilkaset funkcji - ale ja jestem na to za leniwy.
- Rejestracja:około 21 lat
- Ostatnio:około 2 miesiące
- Postów:1082
Patryk27 napisał(a):
Znowu próbujesz sobie torować drogę na skróty :P
Teoretycznie mógłbyś spróbować utworzyć sobie klasę zawierającą metody
GetImie()
,GetNazwisko()
orazGetMiasto()
(przy okazji: nie wykorzystuj polskiego nazewnictwa w kodzie, nigdy) oraz pobawić się z RTTI, natomiast to by było czysto dla zabawy i bardzo nie chciałbym spotkać czegoś takiego w normalnym, produkcyjnym kodzie.
Zatem nigdy przenigdy nie używaj LiveBindings w Delphi.
Trzymaj się z dala od wszelkiej maści ORMów i nomen-omen doskonałego Spring4Delphi lub DSharp.
Dlaczego? Bo tam bardzo intensywnie używa się RTTI, ale dla Ciebie to zabawa, a nie kod produkcyjny.
Jasne, można i tak. Ale można inaczej, lepiej, prościej i szybciej - a przy tym bardziej ogólnie.
wloochacz napisał(a)
Bo tam bardzo intensywnie używa się RTTI, ale dla Ciebie to zabawa, a nie kod produkcyjny.
Jestem ciekaw, w którym to momencie powiedziałem, jakoby jakiekolwiek używanie RTTI było zabawą :-P
Napisałem, że w tym konkretnym przypadku, na moje oko, byłoby to zaciemnianiem kodu - co innego, gdy wykorzystuje się odrębny framework.
- Rejestracja:około 21 lat
- Ostatnio:około 2 miesiące
- Postów:1082
Patryk27 napisał(a):
wloochacz napisał(a)
Bo tam bardzo intensywnie używa się RTTI, ale dla Ciebie to zabawa, a nie kod produkcyjny.
Jestem ciekaw, w którym to momencie powiedziałem, jakoby jakiekolwiek używanie RTTI było zabawą :-P
W tym miejscu:
[...] oraz pobawić się z RTTI, natomiast to by było czysto dla zabawy i bardzo nie chciałbym spotkać czegoś takiego w normalnym, produkcyjnym kodzie.
Napisałem, że w tym konkretnym przypadku, na moje oko, byłoby to zaciemnianiem kodu - co innego, gdy wykorzystuje się odrębny framework.
Nie napisałeś tego - teraz to dopisałeś.
Poza tym, przecież framework to też kod.
A na moje oko, to pisanie n funkcji do tego samego, to jest zaciemnianie kodu.
Tego właśnie OP chciał uniknąć, a Ty radzisz mu coś takiego...
wloochacz napisał(a)
W tym miejscu:
Tak, odnosząc się do tego konkretnego przypadku, a nie wszystkich możliwych sytuacji :-P
A na moje oko, to pisanie n funkcji do tego samego, to jest zaciemnianie kodu.
Podam może zatem przykład takiego genialnego rozwiązania (pozwoliwszy sobie odnieść się do innego języka): PHP, sklep Magento 1.
W PHP można utworzyć w klasach magiczne metody m.in. __get
oraz __set
, które umożliwiają odnoszenie się do klasowych pól bez ich formalnej deklaracji/definicji (te metody służą za akcesory, przyjmując jako parametr nazwę pola, do którego programista w kodzie próbuje się odnieść).
W konsekwencji spora liczba metod tak naprawdę pojawia się w kodzie z tyłka, niemożliwe staje się ich sensowne debugowanie czy skakanie po kodzie, nie mówiąc o braku podpowiadania składni przez IDE, na czym przy początku mojej pracy nieraz się przejechałem (bo przecież IDE nie podpowie literówki w getBaseSubtotalWitDiscount
, skoro potencjalnie każda nazwa jest poprawna, w zależności od wnętrza metody __get
).
O refaktoryzacji takiej metody również można pomarzyć.
Gdyby do każdego pola istniały odrębne akcesory, to faktycznie - codebase powiększyłby się o dodatkowe dziesiątki kilobajtów, natomiast stwarzujmy prawdę:
- Każde sensowne IDE i tak automatycznie potrafi paroma kliknięciami wygenerować oraz zaktualizować potrzebne metody.
- Czytelność kodu oraz możliwość jego statycznej analizy (głównie dla analizy typów) wzrosłaby ogromnie.
Podałbym jakiś adekwatny przykład dla Delphi, lecz dawno temu przestałem już pisać w Pascalu i nic konkretnego nie przychodzi mi na myśl.