.NET - Ukladanie premenných rôznych typov do jednej kolekcie rubrika: Programování: .Net

7 xxar3s
položil/-a 23.8.2019
 
upravil/-a 23.8.2019

Nakolko robim na svojom novom jazyku a teraz riesim jeho behove prostredie mam par nezvycajnych otazok.

Predstavte si ze mate kolekciu a do tejto kolekcie chcete ulozit hociaky typ. V nete sa to robi typicky nejako takto:

let table = new Dictionary<string, obj>() //obj je alias pre System.Object - specifikum jazyka F#
table.Add("Name", box "Sandra")
table.Add("Age", box 29) // funkcia zabali premennu do objektu ktory sme pouzili ako genericky parameter pre value funkcia box v priklade neni potrebna dal som ju tam teraz pre nazornost. 

Ale predstavte si ze ste v jazyku ktory je cisto staticky, a nema take vymozenosti ako jeden bazovy typ (System.Object v .NETe) neda sa teda pouzit ani pretypovanie na objekt. Lenze do Dictionary<'k, 'v> mozte vlozit len premenne jedneho typu. Ako tam teda dostat tych typov viac? Bez pretypovavania a bez boxingu a bez vyuzitia unions ci discriminated unions?

Klasicke riesenie ktore sa pouzivalo v minulosti by bolo vytvorit kolekciu smernikov na void (voidptr) hodnotu hociakeho typu ulozit do premennej a na nu ukazat smernikom (smernik - premenna obsahujuca adresu v pameti ktorou ukazuje na premennu) a tento smernik ulozit do Dictionary<'k, 'v>. U hodnotovych unmanaged typov ktore sa vytvaraju na stacku (int, float, struct) je to jednoduche. Ale co u managed typov? (pre jednoduchost ostaneme v jazyku F#, ale budeme sa tvarit ze nema ziadny zakladny typ).

Mala odbocka pre C#-ckarov ku keywordu use - use je alternativa ku keywordu using z jazyka C#. Ma ale mierne odlisny zapis. V C# nasleduje blok za prikazom a pri jeho skonceni sa uvolnia prostriedky. V F# je vnoreny do bloku (vyrazu, funkcie) a po jej skonceni sa uvolnia prostriedky.

Priklad som teda musel zabalit do funkcie main lebo pri jej skonceni sa uvolnia prostriedky. Ale najprv zacnem unmanaged typom lebo ten je jednoduchsi. behove prostredie .NETu nam dovoluje vytvorit smernik len na mutable variables ktoreho typ musi byt unmanaged (typicky: int, single, double, bool, struct). Alebo na niektore vynimocne managed typy ako string, byte[] char[] vlastne vsetky typy poli na tieto managed typy sa, ale pouziva keyword fixed ktorym sa docasne zafixuje premenna na ktoru vytvorim smernik takze mam istotu ze ju neuprace garbage collector tu je riesenie aj managed aj unmanaged typu:

let main () =
    let mutable age = 29 
    let ptrAge = &&age // vytvorenie pointeru na unmanaged typ integer
    use ptrName = fixed "Sandra" // fixed dovoluje vytvorit smernik len na niektore managed typy a to aj ked su immutable
    let table = Dictionary<string, voidptr>()
    table.Add("Name", ptrName |> NativePtr.toVoidPtr) //nativePtr je pointer nejakeho typu (smernik na char, smernik na byte) a voidptr je smernik na prazdny typ kedze potrebujeme univerzalny nativeptr prekonvertujeme na voidptr
    table.Add("Age", ptrAge |> NativePtr.toVoidPtr)

Riesenie so smernikmi bohuzial neni dostatocne univerzalne lebo funguje iba premennymi unmanaged typov (tie ktore sa vytvaraju na stacku su najmenej problemove lebo sa pamet uvolnuje automaticky aj bez GC).
alebo niektorymi zafixovanymi managed typmi (string, array) ako vidite v priklade. Alebo unmanaged typmi, ktore sa vytvaraju na heape - treti pripad bude nutne pouzit pre vsetky ostatne managed typy ktore bude treba skopirovat a prekonvertovat na unmanaged aby sa na ne dal vytvorit smernik. Alebo treba najst ine riesenie tohoto problemu mna jedno riesenie napadlo ale neviem ci je to moc ciste riesenie. A toto riesenie neobchadza ani GC a je rovnako univerzalne ako pointery, vlastne ich aj ciastocne pouziva pre konverziu ale najprv si pockam ci niekto nema lepsi napad ako je ten moj.

Takze ako by ste vy riesili tento problem?

PS: niektore konstrukcie sa daju v F# napisat aj kratsie, ale napisal som to tak aby to bolo zrozumitelne aj pre programatorov z inych jazykov. Taktiez v F# sa namiesto Dictionary pouziva kolekcia immutable Map<'k, 'v> ale pouzil som dictionary lebo vacsina programatorov z inych jazykov tuto kolekciu dobre pozna.

odkaz
7 harrison314
odpověděl/-a 24.8.2019
 
upravil/-a 25.8.2019
  1. Preco nechces pouzit boxing?
  2. Teoreticky mozes urobit strukturu, kora bude interne ukazovat bud na refencny typ refernciou alebo pointerom (pointer+typ) na hodnotovy typ na stacku.
  3. Ako chces rieist problem, ked vytvoris takuto kolekciu "referncii na hodnoty na stacku" a nasledne bude vratena metodou, pricom na stacku bude uz uplne nieco ine?

Pro zobrazení všech 3 odpovědí se prosím přihlaste:

Rychlé přihlášení přes sociální sítě:

Nebo se přihlaste jménem a heslem:

Zadejte prosím svou e-mailovou adresu.
Zadejte své heslo.