Klávesnice zdarma Klávesnice zdarma
Pořádné programy s pořádnou klávesnicí zdarma. Více zde
Pouze tento týden sleva až 80 % na C# .NET

Diskuze: Tak struct je reference nebo co?!

C# .NET .NET (C# a Visual Basic) Tak struct je reference nebo co?! American English version English version

Aktivity (3)
Avatar
Jakub Hrdoun
Člen
Avatar
Jakub Hrdoun:13.8.2018 11:47

Ahoj,
do dnešního rána jsem žil pokojně v domnění, že když si vytvořím v C#

public struct StatistickaHodnotaStavu
    {
        public string Hodnota { get; set; }

        public ulong Mocnost { get; set; }

        public StatistickaHodnotaStavu(string hodnota, ulong mocnost)
        {
            Hodnota = hodnota;
            Mocnost = mocnost;
        }
    }

a pak budu tenhle struct jednoduše kopírovat, tak se NEbude chovat jako reference.
Nicméně mám další struct s metodou s jednoduchým foreachem:

public struct StatistickyStav{

public List<StatistickaHodnotaStavu> Hodnoty { get; private set; }

public void PridejHodnoty(List<StatistickaHodnotaStavu> noveHodnoty){

foreach (StatistickaHodnotaStavu novaHodnota in noveHodnoty)
                {
                        if (Hodnoty.Exists(h => h.Hodnota == novaHodnota.Hodnota))
                    //jestli stejná hodnota existuje v property Hodnoty, tak přičteme mocnost nové hodnoty k existující hodnotě
                    {
                                                //z property Hodnoty si vytáhnu existující hodnotu
                        StatistickaHodnotaStavu statistickaHodnotaStavu = Hodnoty.First(h => h.Hodnota == novaHodnota.Hodnota);
                                                //PROBLÉM: tady se odebere statistickaHodnotaStavu i z kolekce noveHodnoty
                        Hodnoty.Remove(statistickaHodnotaStavu);

                        statistickaHodnotaStavu.Mocnost += novaHodnota.Mocnost;
                                                //tady se zase přidá
                        Hodnoty.Add(statistickaHodnotaStavu);
                    }
                    else
                    {
                        Hodnoty.Add(novaHodnota);
                    }

                }
}
}

A teď co je špatně:
Na řádku

Hodnoty.Remove(statistickaHodnotaStavu);

se mi odebere statistický stav i z kolekce noveHodnoty. Což podle mého skromného názoru značí, že se struct statistickaHod­notaStavu chová jako reference.
Foreach samozřejme spadne s

System.Invali­dOperationExcep­tion: 'Kolekce byla upravena. Operace výčtu pravděpodobně nebude spuštěna.'

POZN.: Pojmenování proměnných možná není zrovna ideální, ale nerad bych to tady řešil ;). Díky.

Zkusil jsem: Dobře, asi by to šlo nějak přepsat, nicméně rád bych pochopil, proč konkrétně tohle řešení nefunguje. Podle toho, jak chápu chování structů, by totiž fungovat mělo.
Takže asi něco chápu špatně :) .

Chci docílit: Cíl metody PridejHodnoty:
Projet každý prvek z listu co přijde parametrem a jestliže prvek existuje v property Hodnoty, tak přičíst jeho Mocnost (tj. počet výskytů) ke stávající Mocnosti v existujícím prvku v Hodnoty. Jestliže prvek v Hodnoty neexistuje, tak ho tam přidáme s jeho Mocností.

 
Odpovědět 13.8.2018 11:47
Avatar
Luboš Běhounek Satik
Autoredaktor
Avatar
Luboš Běhounek Satik:13.8.2018 12:14

Vsak se ten struct nechova jako reference.
Jako reference se ti chova ten List, protoze to je referencni typ, i kdyz ho naplnis hodnotovyma typama.

A ve foreachi nemuzes tu kolekci upravovat, pokud ji chces upravovat pri prochazeni, tak musis normalnim cyklem nebo druha varianta je prochazet kopii a nebo si udelat pomocny pole, kam si ulozis prvky, co chces potom vymazat.

Osobne bych to cely prekopal rovnou na

Dictionary<string ulong>

Coz bude i o neco rychlejsi, protoze hledani hodnoty, jestli uz tam je, bude mit konstantni slozitost (hledani v hashmape), ted hledas v listu s linearni slozitosti.

Editováno 13.8.2018 12:15
Akceptované řešení
+20 Zkušeností
+1 bodů
Řešení problému
Nahoru Odpovědět 13.8.2018 12:14
https://www.facebook.com/peasantsandcastles/
Avatar
Petr Čech
Redaktor
Avatar
Odpovídá na Jakub Hrdoun
Petr Čech:13.8.2018 12:21

Tohle ale nemá co dělat s tím, jak se chová primitivní nebo referenční typ, tohle je jen o chování listu. Zdá se, že nějakým způsobem dojdeš k situaci, kdy Hodnoty a NoveHodnoty jsou stejná instance - tudíž v momentě, kdy uděláš Hodnoty.Remove() nebo Hodnoty.Add(), změní se noveHodnoty a spadne to na té výjimce.

Jestli se tomu chceš jednoduše vyhnout, můžeš třeba do PridejHodnoty() vždy dávat kopii (prostě zavoláš .ToArray()).

Můžeš tedy spát pokojně dál, protože struct se skutečně jako reference nechová.

Editováno 13.8.2018 12:23
Nahoru Odpovědět  +2 13.8.2018 12:21
the cake is a lie
Avatar
Jakub Hrdoun
Člen
Avatar
Jakub Hrdoun:13.8.2018 15:04

Díky za odpovědi, mám v tom teď trochu víc jasno.

 
Nahoru Odpovědět 13.8.2018 15:04
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 4 zpráv z 4.