Avatar
Jakub Hrdoun
Člen
Avatar
Jakub Hrdoun:13. srpna 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. srpna 11:47
Avatar
Luboš Satik Běhounek
Autoredaktor
Avatar
Luboš Satik Běhounek:13. srpna 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. srpna 12:15
Akceptované řešení
+20 Zkušeností
+1 bodů
Řešení problému
Nahoru Odpovědět 13. srpna 12:14
https://www.facebook.com/peasantsandcastles/
Avatar
Petr Čech
Redaktor
Avatar
Odpovídá na Jakub Hrdoun
Petr Čech:13. srpna 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. srpna 12:23
Nahoru Odpovědět  +2 13. srpna 12:21
the cake is a lie
Avatar
Jakub Hrdoun
Člen
Avatar
Jakub Hrdoun:13. srpna 15:04

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

 
Nahoru Odpovědět 13. srpna 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.