Typy niezmienne i referencyjne w C#

0

Witam, przerabiam książkę "C# 5.0 Programowanie" i nie rozumiem dlaczego ten kod działa nie tak jak ja sobie to wyobrażam:

 using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        class Counter
        {
            private readonly int _count;
            private static int _totalCount;

            public Counter()
            {
                _count = 0;
            }

            public Counter(int counter)
            {
                _count = counter;
            }

            public Counter GetNextValue()
            {
                _totalCount += 1;
                return new Counter(_count + 1);
            }

            public static Counter operator ++(Counter input)
            {
                return input.GetNextValue();
            }

            public int Count
            {
                get { return _count; }
            }

            public int TotalCount
            {
                get { return _totalCount; }
            }
        }


        static void Main()
        {
            Counter c1 = new Counter(); // tworzę nowy obiekt i przypisuję referencję 
            Counter c2 = c1;            // c1 i c2 pokazują na to samo
            c1++;                       // (?)używam operatora ++ ale zwracanej wartości nigdzie nie przypisuje
            Console.WriteLine("c1: " + c1.Count); // (?)moim zdaniem powinno być tutaj 0, a nie 1, dlaczego tak się dzieje? 
            c1++;
            Console.WriteLine("c1: " + c1.Count); // (?)moim zdaniem powinno być tutaj 0
            c1 = c1.GetNextValue();  // tutaj jest okej bo zwracaną wartość przypisuję więc teraz c1 powinno zawierać wartość 1;
            c2++;
            Console.WriteLine("c2: " + c2.Count); // (?)moim zdaniem powinno być tutaj 0

            c1++;
            Console.WriteLine("c1: " + c1.Count); // (?)moim zdaniem powinno być tutaj 1, a jest 4, WTF ?

            Console.ReadKey();
        }
    }
}

Chodzi mi głównie o operator ++, okej zwraca on referencję do nowego obiektu ale ta referencja nie jest nigdzie przypisywana więc z jakiej racji to działa? Moim zdaniem tak powinno być okej:

 c1 = c1++; 

skoro mamy być konsekwentni tak jak wygląda to w wypadku wywołania funkcji:

 c1 = c1.GetNextValue(); 
0

c++ to inaczej c = c + 1;

0

No tak wiadomo, że "c++" to inaczej " c = c + 1" ale tak wygląda mechanizm w typach wbudowanych(liczbowych), a ja tutaj sam definiuję operator ++ i nie modyfikuję obiektu ponieważ nie mogę (słowo kluczowe readonly) tylko zwracam referencję do nowego obiektu, nigdzie uprzednio go nie przypisując a jednak wartość się zmienia.

0
c++;

to skrócona forma

c = c+1; 

Jeśli tylko ten kod jest jako osobna instrukcja. Natomiast można także wykorzystać inkrementację w jakiejś rozbudowanej instrukcji.

 c = c++;

Tutaj najpierw użyje wartości c w owej instrukcji, przez co przypiszesz po prostu tę samą wartość. Następnie wartość zostanie zwiększona, dzięki czemu ostatecznie wartość zostanie zwiększona o jeden, lecz takiego zapisu się nie stosuje.

1

Wykonuję się twój kod z przeciążonego operatora ++ czyli zwiększenie wartości oraz zwrócenie referencji i domyślna logika c++. Proste.

0

Nie rozumiecie co mam na myśli, według mnie wszystko było by okej gdyby konstruktor ++ wyglądał tak:

public static Counter operator++ (Counter input)
{
Counter temp = new Counter(this._count);
this = new Counter(_count + 1);
return temp;
}

Tutaj jawnie w operatorze do obecnej referencji przypisujemy nową referencję zwróconą przez operator 'new', lecz w tamtym kodzie tego nie ma, a jednak wartość jest zwiększana i tego nie rozumiem

zmiana znacznika <code class="csherp"> na <code class="csharp"> - fp

0

Hmm chyba nikt mnie nie rozumie...
Ja dobrze wiem że:

int a = 10;
a = a + 1;

Jest równoważne z:

int a = 10;
a++;

Chodzi o to, że kiedy jawnie przeciążałem operator++ w C++, musiałem zadbać o jego główną cechę, czyli o to że w przetwarzanym wyrażeniu wartość zmiennej się nie zmienia a dopiero po zakończeniu instrukcji, zmienna ma faktycznie wartość zwiększoną o jeden. Robiło się mniej więcej to w ten sposób:

class T
{
//..
int a;
//..
public T operator++()
{
T temp = this; // !!! Przypisuję bieżącą wartość, którą zwrócę do obliczenia wartości przed jej faktycznym zwiększeniem
this.a++;
return temp;
}
}

W przykładzie z książki C# 5.0 Programowanie, w ciele zdefiniowanego operatora++ nie jest robione nic po za zwiększeniem wartości i zwróceniem referencji do nowego typu, ale ta referencja NIE JEST nigdzie przypisywana a wartość mimo to się zmienia.

public Counter GetNextValue()
            {
                _totalCount += 1;
                return new Counter(_count + 1); // !!! Zwraca referencję do nowego obiektu !!
            }

            public static Counter operator ++(Counter input)
            {
                return input.GetNextValue(); // !!! Tutaj po prostu jest zwrócona ta referencja, nie jest nigdzie podmieniana !!!
            }

/* ... */

void Main()
{
Counter c1 = new Counter(); // licznik ma początkową wartość 0;
c1++; // !!! UWAGA, użyłem operatora ++ który nie robi nic oprócz zwrócenia referencji do NOWEGO typu, nie podmieniając sam referencji !!
Console.WriteLine(c1.Count); // jakim cudem tu jest 1 ?
}

Moim zdaniem wszystko było by jasne gdybyśmy zwróconą referencję z operatora++ nadpisali w ten sposób:

c1 = c1++;

Proszę, zwrócić uwagę że są to typy niezmienne, w książce autor chciał zasymulować działanie typy 'string', które polega na tym że stringi są niezmienne. No ale ten operator++, nie rozumiem czym tak się dzieje :( Mam nadzieje że teraz wyjaśniłem się jasno.

dodanie znaczników <code class="csharp"> - fp

0

Hmm chyba nikt mnie nie rozumie...
Rozumie, a ty nie potrafisz zrozumieć że działa tak jak działa, i że tak ma być.

Zacytuję fragment strony, do której dałem linka:

The run-time processing of a postfix increment or decrement operation of the form x++ or x-- consists of the following steps:
If x is classified as a variable:
• x is evaluated to produce the variable.
• The value of x is saved.
• The selected operator is invoked with the saved value of x as its argument.
• The value returned by the operator is stored in the location given by the evaluation of x.
• The saved value of x becomes the result of the operation.

0

Czyli C# działa na innej zasadzie niż C++ i można powiedzieć że "automatycznie" to wszystko robi ?

0

To ty nie możesz zrozumieć, że działa tak jak zostało przedstawione w linku od Azariena.

3
UczącySIę napisał(a):

Czyli C# działa na innej zasadzie niż C++ i można powiedzieć że "automatycznie" to wszystko robi ?

Przede wszystkim, to C# w przeciwieństwie do C++ ma w ogóle zasadę działania w tym przypadku.

0

Ok przepraszam za moje niedociągnięcia, ale tak jak wspomniałem uczę się :)

Czyli żeby już sobie nie mieszać w głowię, poczytałem trochę w necie i chciałbym żeby ktoś potwierdził czy dobrze zrozumiałem, ewentualnie poprawił :)

  1. w C# definiujemy jeden operator ++ do post i pre inkrementacji w odróżnieniu od C++ kiedy musimy zdefiniować dwa różne operatory;
  2. kompilator C# sam wie kiedy nasz operator++ potraktować jako post i pre inkrementację i można powiedzieć, że automatycznie kod:
Counter c1 = new Counter(0);
Counter c2 = c1++;

Zamienia na coś w typu:

Counter c1 = new Counter(0);
Counter c2 = c1;
c1.operator++();

A kod:

Counter c1 = new Counter(0);
Counter c2 = ++c1;

Zamienia na cođ w typu:

Counter c1 = new Counter(0);
c1.operator++();  
Counter c2 = c1;

Czy teraz wszystko dobrze rozumiem?:)

zmiana znaczników <code class="sharp"> na <code class="csharp"> - fp

0

Zauważ że dlatego operator w podanym kodzie zwraca nowy obiekt, inaczej linijka

Counter c2 = c1;

nic by nie robiła (byłoby c1==c2, więc przypisanie nic nie robi), i postinkrementacja by zwracała złą wartość.

0

Nadal mam pewne niejasności, a nienawidzę kiedy czegoś nie rozumiem :( Do rzeczy, mam kod:

class Licznik
        {
            public int value;

            public static Licznik operator ++(Licznik input)
            {
                input.value += 1;
                return input;
            }
        }
// ...

Licznik l1 = new Licznik();
            Licznik l2 = new Licznik();

            Console.WriteLine("l1: " + (l1++).value);
            Console.WriteLine("l2: " + (++l2).value);

            Console.ReadLine();

Dlaczego, zostają wyświetlone dwie 1 ? a najpierw 0 potem 1?

0

To co w nawiasie wykona sie najpierw.

0

Po usunięciu () efekt ten sam, dwie jedynki na wyjściu, zamiast oczekiwanego zera i jedynki. Więc jak to w końcu jest z tymi operatorami w C#?

0

AFAIK musisz zwrocic nowy obiekt w tym operatorze, czyt. cos takiego:

Licznik a = new Licznik(input.value);
a.value += 1;
return a;
0

Już nic nie czaje:( Tutaj piszą coś innego: http://www.codeproject.com/Articles/452727/A-Beginners-Tutorial-on-Operator-Overloading-in-Cs w sekcji:** Overloading the Unary operators**. Może temu nie mogę sobie tego w głowie poukładać bo pozostały mi praktyki po c++.

0

Masz przyklad:

using System;
 
namespace Program
{
    class Licznik
    {
        public int Value { get; set; }
        
        public Licznik() {}
        public Licznik(int a) { Value = a; }
        
        public static Licznik operator++(Licznik l)
        {
            return new Licznik(l.Value + 1);
        }
        
        public static Licznik operator--(Licznik l)
        {
            l.Value -= 1;
            return l;
        }
        
        public override string ToString()
        {
            return Value.ToString();
        }
    }
    
    class Program
    {
        static void Main()
        {
            Licznik l1 = new Licznik();
            Licznik l2 = new Licznik();
            Licznik l3 = new Licznik();
            Licznik l4 = new Licznik();
            
            Console.WriteLine(l1++);
            Console.WriteLine(++l2);
            Console.WriteLine(l3--);
            Console.WriteLine(--l4);
        }
    }
}

Output:

0
1
-1
-1

http://ideone.com/iUYHz4

Jak widac to co Ci powiedzialem (czyli utworzenie nowego obiektu) dziala tak jak oczekujemy.

0

Przecież właśnie napisałem, dlaczego tak się dzieje:

Azarien napisał(a):

Zauważ że dlatego operator w podanym kodzie zwraca nowy obiekt, inaczej linijka

Counter c2 = c1;

nic by nie robiła (byłoby c1==c2, więc przypisanie nic nie robi), i postinkrementacja by zwracała złą wartość.

1 użytkowników online, w tym zalogowanych: 0, gości: 1