Sprawdzanie typów - tablica i foreach

0

Cześć natknąłem się na taki problem. Jak to jest z kontrolą typów w przypadku tablic przeszukiwanych pętlą foreach. Mam tablicę float, a w pętli foreach wartość tablicy zapisuję do zmiennej byte. Według mnie kompilator nie powinien dopuścić do takiej sytuacji bez rzutowania. Czy jest to niedoróbka kompilatora, czy jest to w jakimś celu zamierzone? Dlaczego tak się dzieje? Poniżej kod.

float[] tablica = {5,6,5,4,3000};
foreach (byte element in tablica)
     Console.WriteLine(element);
0

Każda tablica posiada interfejs IEnumerable aby możliwe było zrobić właśnie foreach'a. Dawniej w .NET nie było typów generycznych więc IEnumerable daje dostęp do elementów w postaci Object. byte to też Object, ale rzutowanie się wysypie. Typując foreach'a sam ponosisz odpowiedzialność za zgodność typów.

0
adamB napisał(a)

Dlaczego tak się dzieje?

Występuje rzutowanie nie jawne. W rzeczywistości kompilator na podstawie konstrukcji foteach tworzy pętle while, w której wykorzystuje składowe dwóch interfejsów: IEnumerable oraz IEnumerator. W konstrukcji tej pętli występuje rzutowanie kolekcji to typu zdeklarowanego elementu.

Coś na zasadzie:

IEnumerator enumerator = (IEnumerator)tablica.GetEnumerator()

//w pętli while: 
byte element  =  (byte)enumerator.Current
0

dołączę się do wątku, mam problem z interfejsem IEnumerable.

class Dictionary : IEnumerable<Entry>
    {
        public IEnumerator<Entry> GetEnumerator()
        {
            foreach (Entry e in tab)
            {
                yield return e;
            }
        }
        public int GetNumEntries()
        {
            return ctr;
        }

        private Entry[] tab;
        private int ctr = 0;

        public Entry this[int index]
        {
            get
            {
                return tab[index];
            }
            set
            {
                tab[index] = value;
            }
        }

        public Dictionary(int a)
        {
            tab = new Entry[a];
        }
        public void AddEntry(Entry e)
        {
            tab[ctr] = e;
            ctr++;
        }
    }

niby wydaje się wszystko w porządku, ale wyrzuca mi bląd,
Error 1 '_1.Dictionary' does not implement interface member 'System.Collections.IEnumerable.GetEnumerator()'. '_1.Dictionary.GetEnumerator()' cannot implement 'System.Collections.IEnumerable.GetEnumerator()' because it does not have the matching return type of 'System.Collections.IEnumerator'.

ogólnie wygląda to tak, że mam klasę Dictionary, która ma zawierać tablicę (kolekcji się jeszcze nie nauczyłem ;) ) elementów Entry.
wie ktoś, o co chodzi? Straszne te interfejsy ;)

i drugie pytanie przy okazji, czy gdy będę chciał sobie posortować, to musze się zająć interfejsem IComparable?

z góry dzięki za odpowiedzi :)

0

poczytaj sobie http://4programmers.net/Forum/viewtopic.php?id=162760
musisz zaimplementowac wszystkie metody i wlasciwosci interfejsu taka jest idea interfejsu, po co ci ogolny schemat ktory olewasz? interfejs to pewien ogolny schemat i jesli go cos implementuje, to cos innego ma pewnosc ze to pierwsze cos udostepnia takie, a takie metody i wlasciowsci

TEORIA JEST <ort>NA PRAWDE</ort> WAZNA!

0

sek w tym, ze jesli implementuje sie swoj pierwszy IEnumerable<>, to jest to ... nieoczywiste.

IEnumerable<X> to interfejs wymagajacy dostarczenia m.in GetEnumerator() -- i to zrobiles, jest ok.
Jednak, to co przegapiles to fakt, ze System.Collections.Generic.IEnumerable<X> dziedziczy po System.Collections.IEnumerable (inny namespace, i bez <X>) ktore z kolei wymaga, abyc zaimplementowal IEnumerator GetEnumerator() -- zwroc uwage, ze jest to INNA SYGNATURA niz tej metody ktora masz, ktora wymagalo IEnumerable<X> !

co wiecej... metoda ta sie nazywa tak samo i ma takie same (żadne) parametry - wiec jest jestes w przyslowiowek "*upie", gdyz nie mozesz napisac overloadow takich, aby spelnic oba interfejsy..

rozwiazaniem jest paskudny hack'o'ficzer jezyka C# ktory sie nazywa "explicit interface implentation" i wyglada tak:

class Y : IEnumerable<X>   // btw. pacnij prawym na IEnumerable i nacisnij "implement interface" :))
{
    public IEnumerator<X> GetEnumerator() // normalna metoda implementujaca IEnumerable<X>::GetEnumerator
    {
        ......
    }
    // ta metoda, #1, jest widoczna normalnie, gdy napiszesz obeiktT.GetEnumerator() czy this.GetEnumerator(),
    // zwraca typowany IEnumerator<X>

    IEnumerator System.Collecitons.IEnumerable.GetEnumerator()
            // NIEnormalna metoda, PRYWATNA, implementujaca IEnumerable::GetEn,
            // jedynie dostepna poprzez przerzutowanie obiektu na ten interfejs. NAWET THIS jej nie widzi normalnie!!!!
    {
         return this.GetEnumerator(); 
           // i dlatego wlasnie to NIE wykona stackoverflow, tylko wywola metode #1,
           // a my jestesmy happy, gdyz IEnumerator<X> przez nia zwracany implementuje takze NIEtypowany IEnumerator, ktory tutaj ta metoda ma generowac
    }
           // jesli chcialbys jednak wywolac metode #2, musialbys napisac ((System.Collections.IEnumerator)this).GetEnumerator()
 
}

IMHO, jest to skitrane, ale da sie przywyknac i nauczyc z tym żyć

0
        public IEnumerator<Entry> GetEnumerator()
        {
            foreach (Entry e in tab)
            {
                yield return e;
            }
        }

Jaki to ma sens, takie zagnieżdżanie foreacha?

        public IEnumerator<Entry> GetEnumerator()
        {
           return tab.GetEnumerator();
        }

i tyle. albo coś w tym stylu w każdym razie.

class Dictionary : IEnumerable<Entry>

cała ta klasa na razie nie ma nic czego by nie miało List<Entry>…

0

Dzięki za odpowiedzi, z tej książki z której korzystam ten kod (tzn podobny) uzyskałem, teraz już rozumiem, czemu nie działało.

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