Tablica generyczna - jak to zrobić?

0

Witam wszystkich forumowiczów. Mam pytanie odnośnie jednego z zadanek jakie chciałem rozwiązać. Niestety nie do końca wiem jak się za nie zabrać gdyż ciągle się uczę C#. Poniżej przedstawiam treść tego zadanka. Byłbym wdzięczny za jakąkolwiek pomoc z waszej strony.

Napisać klasę w języku C# obsługującą rzadką tablicę generyczną (np. o rozmiarze 2 000 000 000 elementów) wiedząc, że liczba faktycznie przechowywanych elementów nie przekracza 1000.

0

http://stackoverflow.com/questions/12559730/creating-a-generic-array-at-runtime + mnóstwo innych bardziej lub mniej ciekawych linków w Google pod hasłem: "generic array C#".
Przyznaj się - nie chciało Ci się szukać nie?

0

To owszem widziałem ale nie wiem jak dokładnie to wykonać, gdyż jak pisałem ciągle się uczę tego języka. Dlatego myślałem że ktoś mi w tym pomoże nie odsyłając wyłącznie do innych linków...

0

A co oznacza "rzadka tablica generyczna"? Z naciskiem na "rzadka".
Kompletnie nie rozumiem treści zadania, co tak naprawdę tam trzeba zrobić.

1

Jesli dobrze rozumiem, to nie wiesz jak stworzyc klase generyczne.

Tak w duzym skrocie:

using System;
 
namespace Program
{
    public class Tabliczka<T> where T : new()
    {
        public T[] Tablica { get; set; }
        
        public Tabliczka(uint rozmiar)
        {
            Tablica = new T[rozmiar];
        }
    }
    
    public class Program
    {
        public static void Main()
        {
            Tabliczka<int> t = new Tabliczka<int>(30);
            Console.WriteLine("=> {0}", t.Tablica[4]);
            
            Tabliczka<float> f = new Tabliczka<float>(10);
            Console.WriteLine("=> {0}", f.Tablica[4]);
            
            Tabliczka<Program> p = new Tabliczka<Program>(10);
            Console.WriteLine("=> {0}", p.Tablica[4]);
        }
    }
}

http://ideone.com/jGolSo

W nawiasach trojkatnych jest oznaczany parametr generyczny, ktory jest aliasem dla typu. where oznacza zawezenie liczby przypadkow, w tym przykladzie do wszystkich typow, ktore maja konstruktor domyslny.

0

Proszę o sprawdzenie czy coś takiego jest poprawne odnośnie tego zadanka? Wzorowałem się na przykładach forumowiczów oraz znalezionych w internecie.

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

namespace Generic_Tab
{
    public class GenericArray<T> where T : new()
    {   //W nawiasach trojkatnych jest oznaczany parametr generyczny, ktory jest aliasem dla typu. 
        //"where" oznacza zawezenie liczby przypadkow, w tym przykladzie do wszystkich typow,
        //ktore maja konstruktor domyslny. 
        
        private T[] array;
        
        //Ustawienie rozmiaru tablicy
        public GenericArray(int size)
        {
            array = new T[size];
        }
        
        //Pobranie elementu o indeksie "index" z tablicy
        public T getItem(int index)
        {
            return array[index];
        }
        
        //Ustawienie elementu w tablicy w miejscu o indeksie "index"
        public void setItem(int index, T value)
        {
            array[index] = value;
        }
    }

    class Test
    {
        //Przykład uzycia i obsługi tablicy generycznej
        static void Main(string[] args)
        {
            int size = 6;
            //--------------------------------------------------
            //Deklaracja tablicy int
            GenericArray<int> intArray = new GenericArray<int>(size);
            //Wstawienie wartości do tablicy
            for (int c = 0; c < size; c++)
            {
                intArray.setItem(c, c * c);
            }
            //Pobranie wartości do wyświetlenia
            Console.Write("\nTablica int  = [ ");
            for (int c = 0; c < size; c++)
            {
                Console.Write(intArray.getItem(c) + " ");
            }
            Console.Write("]\n");
            //--------------------------------------------------
            //Deklaracja tablicy char
            GenericArray<char> charArray = new GenericArray<char>(size);
            //Wstawienie charów do tablicy
            for (int c = 0; c < size; c++)
            {
                charArray.setItem(c, (char)(c + 97));
            }
            //Odczyt charów z tablicy na ekran
            Console.Write("Tablica char = [ ");
            for (int c = 0; c < size; c++)
            {
                Console.Write(charArray.getItem(c) + " ");
            }
            Console.Write("]\n");
            Console.ReadKey();
        }
    }
}
0

Za wielkiego sensu ta klasa nie ma.

  1. Po co tworzysz 2 oddzielne metody, skoro moze to zrobic kompilator za Ciebie?
  2. Jaki jest sens tego ograniczenia, w przypadku gdy z niego nie korzystasz w zaden sposob?
0
Lena(R) napisał(a):

A co oznacza "rzadka tablica generyczna"? Z naciskiem na "rzadka".
Kompletnie nie rozumiem treści zadania, co tak naprawdę tam trzeba zrobić.

Rzadka tablica, to informatyczna wersja rzadkiej macierzy. Większość elementów jest pusta (0 albo null).

Ja bym sugerował zacząć od myślenia, bo to jest problem algorytmiczny, a nie implementacyjny. Nie utworzy się przecież tak po prostu tablicy tej wielkości, bo zabraknie pamięci operacyjnej.

0

**n0name_l **to jak Ty byś to rozwiązał? Mógłbyś zademonstrować, byłbym wdzięczny?

somekind masz rację. Ale jak w takim razie byś się za to zabrał? Czytałem wcześniej o reprezentacji macierzy rzadkich w postaci (x,y,z), tzn dla macierzy np 1000x1000 mając jeden element niezerowy np M[345,754]=65 to można zapisać to jako M(345,754,65) żeby nie pamiętać miliona elementów zerowych tylko jeden niezerowy. Czy to o to chodzi?

1

Moja wizja tablicy rzadkiej jest taka - robisz Hashtable w klasie generycznej która przechowuje(wstawiane są do niej) tylko wartości różne od default(T), czyli rożne od wartości domyślnej typu T. Przy pobieraniu elementu o zadanym indeksie, sprawdzasz czy taki klucz istnieje w Hashtable, jeżeli tak, to pobierasz wartość z niej, w przeciwnym wypadku zwracasz wartość domyślną - default(T).

Co do samego działania tablicy generycznej dobrym rozwiązaniem będzie zastosowanie indexera konstruując odpowiednio metody get i set. Tutaj przykład jak to zrobić:
http://msdn.microsoft.com/en-us/library/vstudio/6x16t2tx.aspx

0

Frizz a czy mógłbyś mi to zrobić według tego co opisujesz? Pytam, gdyż chciałbym się tego nauczyć a nie bardzo wiem jak to po Twojemu ugryźć...

0

Chciałem Cię trochę nakierować co byś sam pogłówkował, ale okej. Łap półgotowca :P. Zmodyfikowałem Twoją klasę aby realizowała ideę tablicy rzadkiej. Powinno działać, jednak oprócz metod getItem i setItem dodałbym indexer o którym pisałem wcześniej, żeby można było korzystać z tej klasy jak z normalnej tablicy. Dodatkowo uważam że warto by zaimplementować interfejs IEnumerable, który pozwoliłby latać po niej pętlą foreach i używać na niej zapytań LINQ. Zachęcam do spróbowania :).

     public class GenericArray<T> 
    {   
        private Hashtable array;
        private int size;
       
        public GenericArray(int size)
        {
            array = new Hashtable();
            this.size = size;
        }

        public T getItem(int i)
        {
            return array.ContainsKey(i) ? (T)array[i] : default(T);
        }

        public void setItem(int i, T value)
        {
            if (i >= size || i < 0)
                throw new IndexOutOfRangeException();
            if (!value.Equals(default(T)))
            {
                array[i] = value;
            }
            else if (array.Contains(i))
            {
                array.Remove(i);
            }
        }
    }
0

Frizz wielkie dzięki za ten kawałek kodu. Postaram się zrozumieć jak to działa i dodać do niego indexera o którym wspominałeś, bo widzę że to dobre rozwiązanie byłoby. Jak będę miał gotowe to wrzucę tu kod żebyś ewentualnie mógł sprawdzić czy dobrze zrobiłem. A o tym interfejsie IEnumerable to dopiero będę musiał poczytać bo jeszcze z tego nie korzystałem, podobnie z LINQ - ale wszystko jeszcze przede mną ;)

0

Kurcze coś nie mogę zrobić tego IEnumerable. Wyczytałem że

foreach requires IEnumerable, not IEnumerable<T>
. Pomożecie jeszcze troszkę?

0

Tak na szybko napisalem:

using System;
using System.Collections;
using System.Collections.Generic;
 
namespace Program
{
  class Array<T> : IEnumerable
  {
    private Dictionary<uint, T> array = new Dictionary<uint, T>();
        
    public T this[uint index]
    {
      get { return array[index];  }
      set { array[index] = value; }
    }
        
    public IEnumerator GetEnumerator()
    {
      foreach(KeyValuePair<uint, T> keyValuePair in array)
      {
        yield return keyValuePair.Value;
      }
    }
  }
    
  class Program
  {
    static void Main()
    {
      Array<char> array = new Array<char>();
      array[10] = 'X';
      array[3] = '8';
            
      foreach(char elem in array)
      {
        Console.WriteLine(elem);
      }
    }
  }
}

http://ideone.com/WutXCJ

Metode te mozna jeszcze zaimplementowac, tak:

foreach(T val in array.Values)
{
  yield return val;
}

badz tak:

return array.Values.GetEnumerator();
1

To jest właśnie przykład, w którym IEnumerable<T> będzie lepsze, bo IEnumerable powoduje boksowanie wartości char (konwersję na object i z powrotem), a IEnumerable<T> stałoby się IEnumerable<char> bez konwersji podczas pracy pętli foreach.

0

Kombinowałem, szukałem po internecie i w końcu udało się w taki sposób że zadziałało. Czy ktoś mi może powiedzieć czy to dobrze, bo trochę kodu wyszło z tym IEcollecgtion.

namespace Generic_Tab
{
 
public class GenericArray<T> : ICollection<T>
    {
        private Hashtable array;        //Deklaracja tablicy typu Hashtable
        private int size;

        public GenericArray(int size)
        {
            array = new Hashtable();    //Utworzenie tablicy konstruktorem
            this.size = size;
        }
        public T this[int i]
        {
            get
            {
                return array.ContainsKey(i) ? (T)array[i] : default(T);
            }
            set
            {
                if (i >= size || i < 0)
                    throw new IndexOutOfRangeException();
                if (!value.Equals(default(T)))
                {
                    array[i] = value;
                }
                else if (array.Contains(i))
                {
                    array.Remove(i);
                }
            }
        }


        protected bool _IsReadOnly;
        // Number of elements in the collection
        public virtual int Count
        {
            get
            {
                return array.Count;
            }
        }

        // Flag sets whether or not this collection is read-only
        public virtual bool IsReadOnly
        {
            get
            {
                return _IsReadOnly;
            }
        }

        // Add a business object to the collection
        public virtual void Add(T ArrayObject)
        {
            array.Add(array,size );	// Znalazłem wersję array.Add(ArrayObject) ale wywalało błąd "1 argumrnt"
        }

        // Remove first instance of a business object from the collection 
        public virtual bool Remove(T ArrayObject)
        {
            bool result = false;

            //loop through the inner array's indices
            for (int i = 0; i < array.Count; i++)
            {
                //store current index being checked
                T obj = (T)array[i];

                //compare the BusinessObjectBase UniqueId property
                if (obj.GetHashCode() == ArrayObject.GetHashCode())
                {
                    //remove item from inner ArrayList at index i
                    array.Remove(i);
                    result = true;
                    break;
                }
            }
            return result;
        }

        // Returns true/false based on whether or not it finds
        // the requested object in the collection.
        public bool Contains(T ArrayObject)
        {
            //loop through the inner ArrayList
            foreach (T obj in array)
            {
                //compare the BusinessObjectBase UniqueId property
                if (obj.GetHashCode() == ArrayObject.GetHashCode())
                {
                    //if it matches return true
                    return true;
                }
            }
            //no match
            return false;
        }

        // Copy objects from this collection into another array
        public virtual void CopyTo(T[] ObjectArray, int index)
        {
            throw new Exception(
              "This Method is not valid for this implementation.");
        }

        // Clear the collection of all it's elements
        public virtual void Clear()
        {
            array.Clear();
        }

        // Returns custom generic enumerator for this BusinessObjectCollection
        public virtual IEnumerator<T> GetEnumerator()
        {
            //return a custom enumerator object instantiated
            //to use this BusinessObjectCollection 
            return new ArrayEnumerator<T>(this);
        }

        // Explicit non-generic interface implementation for IEnumerable
        // extended and required by ICollection (implemented by ICollection<T>)
        IEnumerator IEnumerable.GetEnumerator()
        {
            return new ArrayEnumerator<T>(this);
        }
		
		//Klasa do obsługi IEnumeratora
        public class ArrayEnumerator<T> : IEnumerator<T> 
        {
            protected GenericArray<T> _collection; //enumerated collection
            protected int index; //current index
            protected T _current; //current enumerated object in the collection

            // Default constructor
            public ArrayEnumerator()
            {
                //nothing
            }

            // Paramaterized constructor which takes
            // the collection which this enumerator will enumerate
            public ArrayEnumerator(GenericArray<T> collection)
            {
                _collection = collection;
                index = -1;
                _current = default(T);
            }

            // Current Enumerated object in the inner collection
            public virtual T Current
            {
                get
                {
                    return _current;
                }
            }

            // Explicit non-generic interface implementation for IEnumerator
            // (extended and required by IEnumerator<T>)
            object IEnumerator.Current
            {
                get
                {
                    return _current;
                }
            }

            // Dispose method
            public virtual void Dispose()
            {
                _collection = null;
                _current = default(T);
                index = -1;
            }

            // Move to next element in the inner collection
            public virtual bool MoveNext()
            {
                //make sure we are within the bounds of the collection
                if (index++ >= _collection.Count)
                {
                    //if not return false
                    return false;
                }
                else
                {
                    //if we are, then set the current element
                    //to the next object in the collection
                    _current = _collection[index];
                }
                //return true
                return true;
            }

            // Reset the enumerator
            public virtual void Reset()
            {
                _current = default(T); //reset current object
                index = -1;
            }
        }
    }

    class Test
    {
        //Przykład uzycia i obsługi tablicy generycznej
        static void Main(string[] args)
        {
            int size = 6;
            //--------------------------------------------------
            //Deklaracja tablicy int
            GenericArray<int> intArray = new GenericArray<int>(size);
            //Wstawienie wartości do tablicy
            for (int c = 0; c < size; c++)
            {
                intArray[c]=c*c;
            }
            //Pobranie wartości do wyświetlenia
            Console.Write("\nTablica int  = [ ");
            foreach (int s in intArray)
            {
                Console.Write(s + " ");
            }
         Console.Write("]\n");
            //--------------------------------------------------
            //Deklaracja tablicy char
            GenericArray<char> charArray = new GenericArray<char>(size);
            //Wstawienie charów do tablicy
            for (int c = 0; c < size; c++)
            {
                charArray[c] = ((char)(c + 97));
            }
            //Odczyt charów z tablicy na ekran
            Console.Write("Tablica char = [ ");
            foreach (char s in charArray)
            {
                Console.Write(s + " ");
            }
            Console.Write("]\n");
            Console.ReadKey();
        }
    }
}
0

Trochę nie widzę sensu implementacji ICollection, skoro Twoja klasa działa jak tablica a nie kolekcja. Zła wartość zwracana przez właściwość Count - ilość elementów w Twojej tablicy to nie rozmiar Hashtable. W Hashtable są przechowywane tylko wartości inne od domyślnych, a rzeczywistych elementów może być przecież więcej. Śmiał bym nawet twierdzić że ilość elementów to rozmiar tablicy. Przez to w enumeratorze w metodzie MoveNext jest błąd i w rzeczywistości może nie przejść po wszystkich elementach po których przejść powinien. Ja bym zrezygnował z interfejsu ICollection i zamiast tego dał sam IEnumerable<T>. Albo zmień trochę koncepcję swojej klasy żeby działa jak lista a nie jak tablica.

0

Kopiujesz kod bez zrozumienia. Miałeś już dobre rozwiązanie enumeratora z yield return, a skopiowałeś przestarzały i przekombinowany kod z osobną klasą enumeratora.

0

Owszem skopiowałem kod i przerobiłem na swój. Nie do końca go rozumiałem, a chciałbym żeby zrozumieć i się nauczyć. No ale zaczęło mi działać i dlatego zapytałem czy dobrze zrobiłem, żeby wiedzieć czy dobrze zrobiłem i żeby przeanalizować sobie poprawny kod. Nie miałem z tym wcześniej do czynienia a będę potrzebował w innym projekcie wykorzystać dlatego w ten sposób się próbuję uczyć. Ale dzięki Wam wielkie za uwagi na ten temat :)
Dobre rozwiązanie to masz na myśli kod podany przez n0name_l z użyciem yield return z użyciem IDictionary? We wcześniejszej wersji programu koledzy z forum zasugerowali użycie Hashtable i dodanie IEnumerable. Próbowałem to rozwiązać używając IEnumerable<T> ale wyczytałem że foreach potrzebuje IEnumerable bez <T> i się jakoś zamotałem z tym że nie wiedziałem jak to rozwiązać. Dlatego próbowałem to zrobić na ICollection, co się udało ze skutkiem jak powyżej. Czym w ogóle różni się użycie Dictionary od Hashtable? Czyli mówisz że lepiej wykorzystać kod podany przez n0name_l?

0

Udało mi się zrobić z Hashtable oraz na Dictionary:

using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
 
namespace Generic_Tab
{
    class GenericArray<T> : IEnumerable<T>
    {
        //------- Hashtable -------------------------------------------------
        private Hashtable array;        //Deklaracja tablicy typu Hashtable
        private uint size;
 
        public GenericArray(uint size)
        {
            array = new Hashtable();    //Utworzenie tablicy konstruktorem
            this.size = size;
        }
        public T this[uint i]
        {
            get
            {
                return array.ContainsKey(i) ? (T)array[i] : default(T);
            }
            set
            {
                if (i >= size || i < 0)
                    throw new IndexOutOfRangeException();
                if (!value.Equals(default(T)))
                {
                    array[i] = value;
                }
                else if (array.Contains(i))
                {
                    array.Remove(i);
                }
            }
        }
        public IEnumerator<T> GetEnumerator()
        {
            foreach (DictionaryEntry entry in array)
            {
                yield return (T)entry.Value ;
            }
        }
        IEnumerator IEnumerable.GetEnumerator()
        {
            foreach (DictionaryEntry entry in array)
            {
                yield return (T)entry.Value;
            }
        }
        
        //-------- Dictionary ---------------------------------------------------
        /*private Dictionary<uint, T> array;
        private uint size;
 
        public GenericArray(uint size)
        {
            array = new Dictionary<uint, T>();    //Utworzenie tablicy konstruktorem
            this.size = size;
        }
 
        public T this[uint index]
        {
            get 
            {
                return array.ContainsKey(index) ? (T)array[index] : default(T); 
            }
            set 
            {
                if (index >= size || index < 0)
                    throw new IndexOutOfRangeException();
                if (!value.Equals(default(T)))
                {
                    array[index] = value;
                }
                else if (array.ContainsKey(index))
                {
                    array.Remove(index);
                }
            }
        }
 
        public IEnumerator<T> GetEnumerator()
        {
            foreach (KeyValuePair<uint, T> keyValuePair in array)
            {
                yield return keyValuePair.Value;
            }
        }
        IEnumerator IEnumerable.GetEnumerator()
        {
            foreach (KeyValuePair<uint, T> keyValuePair in array)
            {
                yield return keyValuePair.Value;
            }
        }*/
        //----------------------------------------------------------------
    }
 
    class Test
    {
        //Przykład uzycia i obsługi tablicy generycznej
        static void Main(string[] args)
        {
            uint size = 6;
            //--------------------------------------------------
            GenericArray<int> intArray = new GenericArray<int>(size);
            
            for (uint c = 0; c < 4; c++)            //Wstawienie wartości do tablicy
            {
                intArray[c] = (int)(c*c);
            }
            intArray[4] = 0;
            intArray[5] = 7;
 
            Console.Write("\nTablica int  = [ ");   //Pobranie wartości do wyświetlenia
            foreach (int s in intArray)
            {
                Console.Write(s + " ");
            }
            Console.Write("]\n");
 
            //--------------------------------------------------
            GenericArray<char> charArray = new GenericArray<char>(size);
 
            for (uint c = 0; c < size; c++)              //Wstawienie charów do tablicy
            {
                charArray[c] = (char)(c + 97);
            }
 
            Console.Write("Tablica char = [ ");
            foreach (char s in charArray)
            {
                Console.Write(s + " ");
            }
            Console.Write("]\n");
            Console.ReadKey();
        }
    }
}

http://ideone.com/3cKhoq#

Jednak po wydrukowaniu na ekran wyświetla mi elementy z tablicy od końca, zamiast

Tablica int = [ 1 4 9 7 ]
to
Tablica int = [ 7 9 4 1 ]
Czym to może być spowodowane?

Wersja z Dictionary jest zakomentowana. Po odkomentowaniu działa prawidłowo, wszystko jest wyświetlane w takiej samej kolejności, tj.

Tablica int = [ 1 4 9 7 ]

0

Zamiast tak odpisywać w komentarzach postanowiłem dodać swój wpis i kawałek kodu.

Podczas pisania założyłem że:

  • index nie może być mniejszy niż 0
  • pierwszy element wskazuje ma index 0
  • skoro ma być to tablica rzadka wiec iterowanie powinno również zwracać domyślne wartości dla niezdefiniowanych elementów
  • iterowanie jest wykonywane do elementu o najwyższym indeksie
  • można zdefiniować jaka jest domyślna wartość, jeżeli nie brana jest domyślna dla typu elementów kolekcji
    Zastosowanie IEnumerable<T> ma na celu ujednolicenie sposobu poruszania się po kolekcji (element po elemencie, od początku do końca) co wykorzystano w klasach LINQ.
    public class GenericArray<T>
        : IEnumerable<T> // co zonacza że również IEnumerable dla kompatybilności z .NET 1.1
    {
        public GenericArray()
            : this(default(T)) // domyślna wartość dla typu (np. int -> 0, dla klass null) 
        {
        }

        // Pozwala określić domyślną wartość dla elemntów pustych
        public GenericArray(T defaultValue)
        {
            _defaultValue = defaultValue;
        }
        private readonly T _defaultValue;
        private readonly IDictionary<int, T> _items = new Dictionary<int, T>();
        private int _itemsCount; // ile mamy elementów (nie jest to ilość elementów w _items)

        public T this[int index]
        {
            get
            {
                return _items.ContainsKey(index)
                           ? _items[index] // jeżeli nasza wewnętrza tabblica przechwuje wartość dal wskazanj pozycji
                           : _defaultValue; // jezeli jej nie ma
            }
            set
            {
                if (index <= 0)
                {
                    throw new IndexOutOfRangeException();
                }

                // Sprawdzamy czy wartość jest różna od domyślnej.
                if (!EqualityComparer<T>.Default.Equals(_defaultValue, value))
                {
                    _items[index] = value;
                    return;
                }

                // Usuwamy jeśli jest
                if (_items.ContainsKey(index))
                {
                    _items.Remove(index);
                }
            }
        }


        public IEnumerator<T> GetEnumerator() // uzycie yield return "automatycznie tworzy" enumerator
        {
            var index = 0;
            foreach (var item in _items.OrderBy(k => k.Key)) // Wykorzytsanie LINQ do sortowania IDictionarty po kluczu
            {
                while (index < item.Key)
                {
                    index++;
                    yield return _defaultValue;
                }
                index++;
                yield return item.Value;
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator(); // IEnumerator<T> tez implemntuje IEnumerator :)
        }
    }

Wykorzystałem tu kilka elementów .NET

  • !EqualityComparer<T>.Default w ten sposób nie interesuje mnie jak porównywane są elementy czy typ T.
  • OrderBy to metoda LINQ do sortowania. W końcu Dictionary to tez kolekcja a każda kolekcja jest IEnumerable<jakiś typ>, to jakiś typ to KeyValuePair<TKey, TValue> ... jeszcze więcej generyków
  • yield return ułatwia zwracanie elementów kolekcji (gdy metoda ma zwracać IEnumerable<jakis typ="typ">). Kompilator generuje dla nas kod enumeratora
    Poniżej chciałem pokazać kilka przykładów użycia LINQ (.NET 4.0). Żeby nie było ściemy do operacji na kolekcji używam osobnej zmiennej tylko typu IEnumerable<int>:
    var instance = new GenericArray<int>(); // domyślna wartośc pusta
            var instance = new GenericArray<int>(); // domyślna kolekcja pusta
            var instanceEnumerable = (IEnumerable<int>)instance;

            // brak elemntów
            AssertThat(instanceEnumerable.Count() == 0);
            AssertThat(!instanceEnumerable.Any()); // to samo co wyżej ale bez zliczania wszytkich elementów

            instance[9] = 10;
            AssertThat(instanceEnumerable.Count() == 10);
            AssertThat(instanceEnumerable.Any());
            AssertThat(instance[9] == 10);
            AssertThat(instance[4] == 0);
            AssertThat(instanceEnumerable.ElementAt(9) == 10);
            AssertThat(instanceEnumerable.Skip(9).First() == 10);
            AssertThat(instanceEnumerable.ElementAt(4) == 0);
            AssertThat(instanceEnumerable.Skip(4).First() == 0);
            AssertThat(instanceEnumerable.Contains(10)); // Dzieki LINQ
            AssertThat(instanceEnumerable.Last() == 10);

            instance[4] = 5;
            AssertThat(instanceEnumerable.Count() == 10);

            var list = instance.ToList(); // Zapisanie danych w nowej tablicy (wszystkich, nawet pustych)
            AssertThat(list.Count == 10);
            AssertThat(list[9] == 10);
            AssertThat(list[4] == 5);

            instance[9] = 0;
            AssertThat(instanceEnumerable.Count() == 5);
            AssertThat(instanceEnumerable.Any());
            AssertThat(instance[4] == 5);

            instance[4] = 0;
            AssertThat(instanceEnumerable.Count() == 0);
            AssertThat(!instanceEnumerable.Any());

gdzie meteoda AsssertThat to taki prosty silnik testujący aby nie wprowadzać żadnego farmeworka i nie gmatwać przykładu:

        public void AssertThat(bool isValid)
        {
            if (!isValid)
            {
                const string message = "Assertion failed.";

                Console.WriteLine(message);
                throw new Exception(message);
            }
        }

Implementacja ma pewne luki, ale one wynikają z założeń. Np. taka operacja przejdzie nawet na nowo stworzonej instancji.

AssertThat(instance[999] == 0);

ale

instance.Count() = 0);

No tak Count() (metoda LINQ nie ta z ICollection) zwraca nam 0 a tu mamy 1000 element. Tak działa przedstawiona implementacja. Nie ma definicji rozmiaru tablicy a Count() przechodzi po wszystkich elementach i je zlicza.
No własnie... IEnumerable<T> pozwala nam przechodzić po kolekcji elementów niezależnie od implementacji... ale tylko tyle nie pozwala nam zarządzać ta kolekcją. Do tego jest służy interfejs ICollection<T> pozwalając nam Dodawać, Usuwać, podmieniać czy dowiedzieć się ile mamy elementów. O ile chcielibyśmy aby nasza klasa mogła zastąpić na przykład klasę List<T> wtedy powinniśmy się nad zastanowić nad jej implementacją. Wspominając o tym interfejsie bardziej chciałem wskazać dalszy kierunek rozwoju implementacji a przy okazji rozważań o zachowaniu implementacji (np to zliczanie). "Dobra praktyka" mówi tylko o tym że każdy typ reprezentujący kolekcję powinien implementować IEnumerable<T>.

Mistzzz'u, dobrze ze piszesz kod pokazujący testowanie, to też "dobra praktyka". Może zainteresuje Cię nUnit lub xUnit albo MSTest. To jak je napisałem to takie małe demo aby je nieco zautomatyzować automatyzować ;).

1

yield return
ułatwia zwracanie elementów kolekcji (gdy metoda ma zwracać IEnumerable<jakis typ="typ">). Kompilator generuje dla nas kod enumeratora

yield return sprawia wrażenie jakby wypychało jedną wartość do docelowej kolekcji, czyli czegoś w rodzaju

result[i++] = wartość;

ale robi co innego: wychodzi z funkcji (w końcu to return...) zwracając podaną wartość, po czym wykonuje się jedna iteracja pętli foreach na tej wartości.
na początku następnej iteracji foreacha funkcja iteratora jest wznawiana od miejsca gdzie było yield return.
Czyli dla przykładowej kolekcji {2, 3, 4, 5} i takiej pętli:

foreach (int i in tab)
{
    Console.WriteLine(i);
}

prawdziwa kolejność instrukcji byłaby następująca:

yield return 2;
Console.WriteLine(2);
yield return 3;
Console.WriteLine(3);
yield return 4;
Console.WriteLine(4);
yield return 5;
Console.WriteLine(5);

Można powiedzieć że iterator działa „na żywo” podczas pracy pętli foreach, a nie że najpierw zbiera elementy na których potem będzie jechać pętla.

0

No to po kilku dniach przerwy udało mi się zrobić program tak aby działał i wszystko wyświetlał jak należy. Poniżej kod:

//-------- Dictionary ---------------------------------------------------
        private readonly Dictionary<uint, T> _array;
        private readonly T _defaultValue;
        private uint _size;
 
        public GenericArray(uint size, T defaultValue)
        {
            _array = new Dictionary<uint, T>(); //Utworzenie tablicy konstruktorem
            this._size = size;
            _defaultValue = defaultValue;       //Określenie wartości dla elementów pustych
        }
 
        public T this[uint index]
        {
            get 
            {
                return _array.ContainsKey(index) ? (T)_array[index] : _defaultValue; 
            }
            set 
            {
                if (index >= _size)
                    throw new IndexOutOfRangeException();
 
                // Czy wartość jest różna od domyślnej, jeśli tak to wstawiamy
                if (!EqualityComparer<T>.Default.Equals(_defaultValue, value)) 
                {
                    _array[index] = value;
                    return;
                }
 
                // Jeśli jest jakaś wartość to ją usuwamy
                else if (_array.ContainsKey(index))
                {
                    _array.Remove(index);
                }
            }
        }
 
        public IEnumerator<T> GetEnumerator()
        {
            SortedDictionary<uint, T> sortKey = new SortedDictionary<uint,T>(_array);
            uint index = 0;
            uint help = 0;
 
            foreach (KeyValuePair<uint, T> item in sortKey)
            {
                while (index < item.Key)
                {
                    index++;
                    help = item.Key ;
                    yield return _defaultValue; //Zwrócenie wartości domyślnych typu T (niezapisane w tablicy)
                }
                index++;
                help = item.Key;
                yield return item.Value;        //Zwrócenie wartości z tablicy generycznej (zapisanych w niej)
            }
 
            for (index = help + 1; index < _size; index++)
            {                                   //Zwrócenie wartości domyslnych typu T niezapisanych w tablicy
                yield return _defaultValue;     //dla przypadku, gdy ostatni niedomyśly element wcale nie jest
            }                                   //ostatnim elementem w tej tablicy 
        }
 
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
        //----------------------------------------------------------------

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