Avatar billede thjal Nybegynder
25. september 2007 - 00:01 Der er 9 kommentarer og
1 løsning

List - hvad er forsellen på for / foreach?

Jeg har følgende kode hvor LOfMyInt32 er en static collection af List. Den første for-løkke fungerer i det at den får ændret indholdet i Listen. De to andre (som er udkommenteret) compiler og kører OK, men ændringerne som InternalUpdate1() skulle udføre på objektet bliver på en eller anden måde ikke gemt i objektet. InternalUpdate1 overføre bare værdien af en private int til en anden private int. 

            for (int i = 0; i < LOfMyInt32.Count; i++)
            {
                MyInt32 temp = LOfMyInt32[i];
                temp.InternalUpdate1();
                LOfMyInt32[i] = temp;
            }
            //for (int i = 0; i < LOfMyInt32.Count; i++)
            //{
            //    LOfMyInt32[i].InternalUpdate1();
            //}

            //foreach (MyInt32 i in LOfMyInt32)
            //{
            //    i.InternalUpdate1();
            //}

Hvorfor fungere 2 + 3 ikke som løsning nummer 1? og er der en mere elegant måde (speed is essential!!!) hvorved jeg kan gøre det samme? Det skal måske lige nævens at jeg har prøvet mig frem med List.ForEach() - men uden held :(
Avatar billede arne_v Ekspert
25. september 2007 - 00:21 #1
Hvis MyInt32 er en value type (struct) så skal de ikke virke !
Avatar billede thjal Nybegynder
25. september 2007 - 08:46 #2
Hvordan kan det være? Men det vil sige at de to nedenstående for løkker ville virke hvis MyInt32 var af typen class?
Der er ikke en hurtigere metode end den jeg benytter?
Avatar billede nielle Nybegynder
25. september 2007 - 11:35 #3
Ja, de ville fungere hvis du ændre struct til class i din definition.

Hvorfor?

Problemstillingen er faktisk den samme som, hvis du kalder en metode med hhv. en struct eller en class – i det første tilfælde overføres argumenterne som kopier, i det andet tilfælde som referencer.

Eksempel 1 - class-typer bliver reference overført:

    class Program
    {
        static void Main(string[] args)
        {
            MyClassType myClass = new MyClassType(7-9-13);
            DoSomething(myClass);
            Console.WriteLine(myClass.tal);
        }

        static void DoSomething(MyClassType argument)
        {
            // Her arbejder vi direkte med selve myClass objektet
            argument.tal = 42;
        }
    }

    class MyClassType
    {
        public int tal;
        public MyClassType(int tal) { this.tal = tal; }
    }

Eksempel 2 - stuct-typer bliver kopi overført:

    class Program
    {
        static void Main(string[] args)
        {
            MyStructType myStruct = new MyStructType(7-9-13);
            DoSomething(myStruct);
            Console.WriteLine(myStruct.tal);
        }

        static void DoSomething(MyStructType argument)
        {
            // Her arbejder vi med en kopi af selve myStruct objektet
            argument.tal = 42;
        }
    }

    struct MyStructType
    {
        public int tal;
        public MyStructType(int tal) { this.tal = tal; }
    }

Det er præcis den samme mekanisme som er på spil i dine løkker.

Struct-tilfældet: De to sidste løkker laver en (temporær) kopi af listens elementer, og det er på kopierne at du kalder din InternalUpdate1() – ikke på selve liste elementet. Da kopierne prompte smides væk ser det ikke ud som om at der sker noget som helst, og da i hvert fald slet ikke med listens elementer.

Class-tilfældet: De to sidste lister laver en reference til listens elementer. Når du kalder din InternalUpdate1() vil den derfor ske direkte mod elementet i listen, og den vil derfor slå igennem.

Begge tilfælde: I din første løkke oprettes der også en kopi (temp) og det er på denne at InternalUpdate1() kaldes. Men i stedet for at smide den væk, indsætter du nu den opdaterede kopi i stedet for den gamle i listen.

Derfor!
Avatar billede nielle Nybegynder
25. september 2007 - 19:02 #4
Den kan vist forklares lidt mere simpelt...

Ved en tildeling:

a = b;

vil man i struct-tilfældet få en kopi af b i a, hvorimod man i class-tilfældet vil få en reference til samme variabel:

Class:

MyInt32 b = new MyInt32(7-9-13);
MyInt32 a = b;  // a peger nu på b.
a.InternalUpdate1();  // Både a og b opdateres!

Struct:

MyInt32 b = new MyInt32(7-9-13);
MyInt32 a = b;  // a peger nu på en kopi af b - men ikke b selv.
a.InternalUpdate1();  // Kun a opdateres!

Relevansen til dine løkker er at der jo netop er en tildeling i denne. Dette er mest tydeligt i foreach-løkken:

foreach (MyInt32 i in LOfMyInt32)
{
    i.InternalUpdate1();
}

hvor i netop tildeles hver af værdierne i LOfMyInt32 undervejs i løkken-gennemgangen.´Men det sker også i din for løkke.
Avatar billede arne_v Ekspert
26. september 2007 - 01:29 #5
Der er dem som mener at ens structs altid bør laves immutable.

Jeg er ikke helt enig.

Men dette spørgsmål er ihvertfald et godt argument for det.
Avatar billede arne_v Ekspert
26. september 2007 - 01:31 #6
Og så en lille terminologisk note:

kald med struct typer er default "by value"

kald med class typer er default "reference by value"

"reference by value" er ikke det samme som "by reference"
Avatar billede thjal Nybegynder
27. september 2007 - 11:53 #7
Nu spørger jeg måske lidt hovedløst; men hvis jeg lavede min liste om til et array - ville jeg så opleve det samme? nielle: ud fra din forklaring ville jeg jo selv svare at det ville jeg...
Avatar billede arne_v Ekspert
27. september 2007 - 16:23 #8
Med array ville for "virke" men foreach ville ikke "virke".
Avatar billede thjal Nybegynder
28. september 2007 - 15:29 #9
godt så - nu tror jeg at jeg er ved at fange det. Nielle, jeg tror denne her går til dig, så hvis du lige kaster et svar ind så skal jeg tildele dig nogle point...
Avatar billede nielle Nybegynder
28. september 2007 - 22:44 #10
Svar :^)
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester