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);
Warto 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.
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.
Następnym razem omówię generyczne interfejs