Generic

Poprzednim razem utworzyłem własną klasę generyczną, która działa jak drzewa binarne. W .NET można też utworzyć generyczną metodę od T.

W generycznej metodzie możesz określić typ parametrów i typ zwracany jako T, w podobny sposób jak wcześniej użyłem tego zapisu do deklaracji klasy generycznej.

Generyczne metody są zazwyczaj używane w połączeniu z generycznymi klasami. W końcu, w takich metodach zazwyczaj parametrem i typem zwracanym jest klasa generyczna.

Generyczne metody są definiowane w podobny sposób jak generyczne klasy. Na przykład, możesz użyć metody Swamp<T> , która zamienia wartości pomiędzy parametrami. Ta funkcja jest użyteczna bez względu na typ danych. Sama metoda może też być statyczna.

static class GenericTool
{
    public static void Swap<T>(ref T first,ref T second)
    {
        T temp = first;
        first = second;
        second = temp;
    }
}

Samo wywołanie metody określa, z jakim typem parametru metoda musi działać. Oto przykład użycia tej metody z int i string.

int x = 0, y = 5;
GenericTool.Swap<int>(ref x, ref y);

string s1 = "0",s2 = "5";
GenericTool.Swap<string>(ref s1,ref s2);


ŻarówkaWarto zauważyć, że tak, jak przy tworzeniu instancji klasy generycznej z różnymi typami parametrów i tutaj każde inne użycie metody Swap, tworzy w kompilatorze inną wersje metody.

Swap<int> to nie jest ta sama metoda, co Swap<string>. Obie metody zostały tylko utworzone, a raczej wygenerowane przez tą samą metodę generyczną. Co znaczy, że obie metody wykazują podobne zachowania pomimo różnicy typów.

Definiowanie Generycznej Metody


Generyczną klasę Tree, która reprezentuje drzewa binarne od T utworzyłem w poprzednim wpisie. Obecnie klasa ta posiada metodę Insert() do umieszczenia pojedynczych wartości.

Projekt który został utworzony w poprzednim wpisie

Dzisiaj do tej klasy dodam metodę generyczną, która umożliwi dodanie wielu wartości do drzewa binarnego. Pod koniec w aplikacji konsolowej przetestuję działanie tej metody.

Chciałem najpierw dodać tę metodę do klasy Tree, ale nawet użycie statycznej metody w klasie generycznej wymaga zadeklarowania typu T.

Do projektu BinaryTree dodałem więc statyczną klasę TreeStaticMethods.

static class TreeStaticMetods
{

}

Definicja metody wygląda tak. Metoda statyczna powinna przyjmować instancje drzewa, jak i zbiór parametrów od T. Magię słowa kluczowego params, wyjaśniłem w tym wpisie.

public static void InsertIntoTree<T>(Tree<T> tree,params T[] data )
{

}

Obecnie ta deklaracja wywoła niezgodność typów T, pomiędzy klasą Tree a metodą InsertIntoTree. Typ T w klasie Tree musi implementować interfejs IComparable<TItem>. Ten warunek jest określony za pomocą słowa kluczowego where, po sygnaturze metody.

public static void InsertIntoTree<T>(Tree<T> tree,params T[] data )
    where T :IComparable<T>
{

}

Pozostało już tylko ciało tej metody. Najpierw muszę sprawdzić, czy liczba argumentów nie będzie wynosić zero. Jest to wymóg, więc gdy, tak się zdarzy, warto wyrzucić wyjątek.

Później w pętli foreach, która przejdzie po każdym parametrze w argumencie metody, wywołam metodę Insert z instancji drzewa podanego w argumencie.

public static void InsertIntoTree<T>(Tree<T> tree,params T[] data )
    where T :IComparable<T>
{
    if (data.Length == 0)
        throw new ArgumentException("BinaryTree:Tree:” + "InsertIntoTree: Must provide at least one data value");

    foreach (T item in data)
    {
        tree.Insert(item);
    }
}

Dla przejrzystości kodu typ T nazwałem TItem. Dla kompilatora to żadna różnica.

public static void InsertIntoTree<TItem>(Tree<TItem> tree, params TItem[] data)
where TItem : IComparable<TItem>
{
    if (data.Length == 0)
        throw new ArgumentException("BinaryTree:Tree:" + "InsertIntoTree: Must provide at least one data value");

    foreach (TItem item in data)
    {
        tree.Insert(item);
    }
}

Pozostało przetestować działanie tej metody w praktyce. W aplikacja konsolowej.

Console.Clear();
Tree<char> tree3 = new Tree<char>('N');
TreeStaticMetods.InsertIntoTree<char>
    (tree3, 'Z', 'B', 'G', 'I', 'E', 'W');


StringBuilder sb3 = new StringBuilder();
Console.WriteLine(tree3.WalkTree(sb3, false));
Console.ReadKey();


Wszystko działa jak należy.

image

Następnym razem omówię generyczne interfejs