Problem z zadaniem z indekserami

0

Witam wszystkich
Jakiś czas temu zacząłem przerabiać kurs z C# i lecę po kolei zadanka. Aktualnie mam pewien problem - jestem początkujący, więc proszę o wyrozumiałość.
Wklejam treść zadania :

Firma w której pracujesz dostała zlecenie na napisanie programu wspierającego sprzedaż. Twoim
zadaniem jest stworzenie klasy reprezentującej fakturę. Faktura zawiera spis towarów.
Musisz umożliwić odwołanie się do każdego towaru znajdującego się na liście przy pomocy
indeksu lub jego nazwy, z tym że przy pomocy nazwy ma być możliwe jedynie pobieranie informacji
o towarze (bez możliwości modyfikacji).

Pogrubiłem to, z czym mam problem(niestety zarówno po nazwie, jak i indeksie można zmieniać wartości pól). Będę bardzo wdzięczny za jakąkolwiek pomoc, bo nie mam pomysłu jak to zrobić.

Poniżej wklejam to co udało mi się napisać:

namespace Lab3_zad1
{
    class Towar
    {
        public string Nazwa { get; set; }
        public string Jednostka { get; set; }
        public double CenaJednostkowaNetto { get; set; }
        public double CenaJednostkowaBrutto
        {
            get { return CenaJednostkowaNetto + (ProcentPodatek / 100) * CenaJednostkowaNetto; }
        }
        public int Ilosc { get; set; }

        public double CenaNetto
        {
            get { return CenaJednostkowaNetto * Ilosc; }
        }
        public double CenaBrutto
        {
            get { return CenaJednostkowaBrutto * Ilosc; }
        }
        public double ProcentPodatek { get; set; } // procent, jaki w cenie brutto stanowi podatek

        public Towar(string Nazwa, string Jednostka, double CenaJednostkowaNetto, int Ilosc, double podatek)
        {
            this.Nazwa = Nazwa;
            this.Jednostka = Jednostka;
            this.CenaJednostkowaNetto = CenaJednostkowaNetto;
            this.Ilosc = Ilosc;
            this.ProcentPodatek = podatek;
        }

        public void WypiszDane()
        {
            Console.WriteLine("Nazwa: {0}\nJednostka: {1}\nJednostkowa cena netto: {2}\nJednostkowa cena brutto: {3}\nIlosc: {4}\nCena netto: {5}\nCena brutto: {6}\nPodatek: {7} % ceny brutto ",
               Nazwa, Jednostka, CenaJednostkowaNetto, CenaJednostkowaBrutto, Ilosc, CenaNetto, CenaBrutto, ProcentPodatek);
        }


    }

    class Faktura
    {
        public List<Towar> lista;
        public DateTime DataSprzedazy { get; set; }
        public int TerminZaplaty //wyrazony w liczbie dni
        {
            get { return Zaplata.DayOfYear - DataSprzedazy.DayOfYear; }
        }
        public DateTime Zaplata { get; set; } //data zapłaty
        public double CenaCalkowitaNetto { get; set; }
        public double CenaCalkowitaBrutto
        {
            get
            {
                double tmp = 0;
                for (int i = 0; i < lista.Count; i++)
                {
                    tmp += lista[i].CenaJednostkowaBrutto;
                }
                return tmp;
            }
        }
    //    public static int nrFaktury = 0; 

        public Faktura(int rokSprzedazy, int miesiacSprzedazy, int dzienSprzedazy, int rokZaplaty, int miesiacZaplaty, int dzienZaplaty)
        {
            lista = new List<Towar>();
            this.DataSprzedazy = new DateTime(rokSprzedazy, miesiacSprzedazy, dzienSprzedazy);
            this.Zaplata = new DateTime(rokZaplaty, miesiacZaplaty, dzienZaplaty);
            for (int i = 0; i < lista.Count; i++)
            {
                this.CenaCalkowitaNetto += lista[i].CenaNetto;
            }

        }

        public Towar ZnajdzTowar(int indeks)
        {
            int i = 0;
            Towar tmp = lista[i];
            while (tmp != null && i < indeks)
            {
                i++;
                tmp = lista[i];
            }
            if (tmp == null)
                throw new IndexOutOfRangeException("Towar o takim indeksie nie istnieje.");

            return tmp;
        }

        public Towar ZnajdzTowar(string nazwa)
        {
            int i = 0;
            Towar tmp = lista[i];
            while (tmp != null && lista[i].Nazwa.Equals(nazwa) == false)
            {
                i++;
                tmp = lista[i];
            }
            if (tmp == null)
                throw new IndexOutOfRangeException("Towar o takim indeksie nie istnieje.");

            return tmp;
        }


        public Towar this[int indeks]
        {
            get { return ZnajdzTowar(indeks); }
            set
            {
                //ZnajdzTowar(indeks) = value;
                ZnajdzTowar(indeks).Nazwa = Convert.ToString(value);
                ZnajdzTowar(indeks).Jednostka = Convert.ToString(value);
                ZnajdzTowar(indeks).CenaJednostkowaNetto = Convert.ToDouble(value);
                ZnajdzTowar(indeks).Ilosc = Convert.ToInt32(value);
            }

        }

        public Towar this[string nazwa]
        {
            get { return ZnajdzTowar(nazwa); }
        }

      
    }
} 

Domyślam się, że gdy zwraca nowy obiekt Towar z indeksera, to wtedy korzysta z właściwości jakie są w nim(obiekcie) zawarte i dlatego w obu przypadkach da się zmienić wartości. Nie wiem jak to ominąć ;/

0

Zwracaj głęboką kopie. A najlepiej jak by obiekt był nie zmienialny, na tej samej zasadzie jak string. Jeżeli nie masz żadnych referencji to możesz także użyć struktury.

0

Zrobiłem głęboką kopię i śmiga jak należy. Dzięki wielkie za pomoc :)

0

Kilka ogólnych uwag do kodu.

Nie używaj nazw typu tmp, w CenaCalkowitaBrutto możesz zrobić "suma".

ZnajdzTowar znowu masz tmp, po co Ci ona w ogóle? I czemu w ogóle korzystasz w tej metodzie z while? Zrób:

if ( lista.Count > index && lista[index] != null )
	return lista[index];
else
	throw new IndexOutOfRangeException("Zły indeks");

A najlepiej po prostu...

return lista[index]'

... to nie Twoja wina, że lista[6] jest nullem i wcale to nie oznacza, że tego indeksu nie ma. :)

Poza tym rzuca się w oczy powtórzenie Cena - może stwórz nową klasę której zadanie będzie przechowywać cenę i wyliczać brutto/netto?

0

Trochę to zadanie bez sensu.
Po pierwsze dlatego, że Faktura i Towar powinny być powiązane ze sobą za pomocą klasy PozycjaFaktury, która posiada właściwości typu Towar, Ilosc, CenaJednostkowa...
Po drugie dlatego, że settery właściwości klasy Towar powinny być po prostu private, jeśli mają nie być modyfikowane. Ustawienie wartości możliwe by było wówczas jedynie w konstruktorze.

A co do kodu:

  1. public Towar(string Nazwa, string Jednostka, double CenaJednostkowaNetto, int Ilosc, double podatek) - nazwy parametrów zaczynamy mała literą;
  2. Metoda WypiszDane nie powinna się znajdować w klasie Towar. Dlaczego niby ta klasa ma wiedzieć coś o tym, że jest częścią aplikacji konsolowej? Dużo lepiej byłoby tutaj przeciążyć ToString.
  3. public Faktura(int rokSprzedazy, int miesiacSprzedazy, int dzienSprzedazy, int rokZaplaty, int miesiacZaplaty, int dzienZaplaty) - dlaczego sześć intów, a nie nie dwa obiekty DateTime?
  4. A to to w ogóle jest jakiś wybuchowy WTF:
public Towar this[int indeks]
{
    get { return ZnajdzTowar(indeks); }
    set
    {
        //ZnajdzTowar(indeks) = value;
        ZnajdzTowar(indeks).Nazwa = Convert.ToString(value);
        ZnajdzTowar(indeks).Jednostka = Convert.ToString(value);
        ZnajdzTowar(indeks).CenaJednostkowaNetto = Convert.ToDouble(value);
        ZnajdzTowar(indeks).Ilosc = Convert.ToInt32(value);
    }

}

Testowałeś to chociaż? ;)

0

Dzięki za wskazówki, oczywiście się z nimi zgadzam. Ten kod co wysłałem był jeszcze nie dopracowany i rzeczywiście zawierał sporo błędów. Co do Ad.4, faktycznie było to bez sensu :D Teraz mi to w miarę dobrze działa. Mam jednak wątpliwości co do tego, że settery właściwości klasy Towar powinny być ustawione na private, ponieważ ma być możliwość zmiany wartości pół po odwołaniu za pomocą indeksu. Nie można zmieniać gdy odwołujemy się za pomocą nazwy. Wtedy wydaje mi się, że w obu wypadkach nie będzie można tego zrobić. Nie wiem jeszcze za bardzo jak zrobić termin zapłaty wyrażony w liczbie dni(tzn. ile dni jest na zapłatę). Jeżeli wpisuję w dacie sprzedaży i zapłaty ten sam rok, można obliczyć to za pomocą " return Zaplata.DayOfYear - DataSprzedazy.DayOfYear; ". Co zrobić w wypadku gdy są wpisane różne lata?

0
kolaxxx napisał(a):

Mam jednak wątpliwości co do tego, że settery właściwości klasy Towar powinny być ustawione na private, ponieważ ma być możliwość zmiany wartości pół po odwołaniu za pomocą indeksu. Nie można zmieniać gdy odwołujemy się za pomocą nazwy. Wtedy wydaje mi się, że w obu wypadkach nie będzie można tego zrobić.

Tak, z tym masz rację, po prostu to zadanie jest przekombinowane. Ja pisałem bardziej o ogólnym przypadku.

Nie wiem jeszcze za bardzo jak zrobić termin zapłaty wyrażony w liczbie dni(tzn. ile dni jest na zapłatę). Jeżeli wpisuję w dacie sprzedaży i zapłaty ten sam rok, można obliczyć to za pomocą " return Zaplata.DayOfYear - DataSprzedazy.DayOfYear; ". Co zrobić w wypadku gdy są wpisane różne lata?

Odjąć jedną datę od drugiej. Otrzymasz wynik typu TimeSpan, w którym liczba dni jest jedną właściwością.

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