params

W tym wpisie dowiesz się:

Jak napisać metodę, która akceptuje jakąkolwiek liczbę argumentów używając słowa kluczowego params.

 

Tablica parametrów jest użyteczna, jeśli chcesz napisać metodę, która pobiera jakąkolwiek liczbę parametrów, możliwe innych typów. Znając koncept obiektowości ta koncepcja może wydawać się   nie trafiona. W końcu obiektowo zorientowane rozwiązania takich problemów polegają na przeciążaniu metod.
Przeciążanie metod pozwala nam na deklarowanie dwóch lub więcej metod, które mają taką same nazwę w danym zakresie. Mechanizm przeciążania jest użyteczny, jeśli chcesz przeprowadzić tę samą akcje dla różnych argumentów.

Mimo iż ten mechanizm jest użyteczny zdarzają się przypadki, w których nie zawsze sobie radzi.
W sytuacji, kiedy typ argumentów nie jest ważny ,a ważna jest ich ilość. Przykładowo powiedzmy ,że chcesz napisać metodę, która ma wyświetlać nieokreśloną ilość parametrów w konsoli. Używając mechaniki przeciążania napisałbyś kilka wersji tej metody ,ale co jeśli użytkownik chciałby podać o jeden argument więcej niż ostatnia wersja przyjmuje. Stosując ten mechanizm zawsze będziesz o jeden krok do tyłu.   Co gorsze masowa duplikacja jednej metody wyglądałaby koszmarnie.
W takiej sytuacji pojawia się bohater tego wpisu: słowo kluczowe params oraz tablice.

Używanie tablicy argumentów

Powiedzmy ,że chcesz napisać metodę, która ma zwrócić długość najdłuższego napisu ze zbioru argumentów string. Jednym ze sposobów jest użycie tablicy.
Przykładowo napisałem statyczną metodę, która pobiera tylko tablicę parametrów typu string do  tego właśnie celu.

class Tools
{
    public static int LongestStringLenght(string[] paramList)
    {
        if (paramList == null || paramList.Length = 0)
        {
            throw new ArgumentException
            ("Tools.LongestString:Not enought arguments")
        }
        int currentMaxLenght = paramList[0].Length;

        foreach (string item in paramList)
        {
            if (item.Length > currentMaxLenght)
            {
                currentMaxLenght = item.Length;
            }
        }
        return currentMaxLenght;
    }
}

Warto obsłużyć i wyrzucić tutaj wyjątek “Argument Exception”, ponieważ ,aby operować na tablicy musi mieć referencje i musi mieć przynajmniej jeden element.

Swoją drogą jest to tylko przykład, ponieważ ten sam efekt może być uzyskany poprzez użycie metody MAX z LINQ. W jednej linijce kodu.

int currentMaxLenght = paramList.Max().Length;

Dla trzech argumentów metoda będzie wyglądać tak.


string[] array = new string[3];
array[0] = "Tomek";
array[1] = "Zdzisław"; //8
array[2] = "Kaśka";

int max = Tools.LongestStringLenght(array);//8

A dla czterech tak. Użyłem tutaj innego stylu dodawania wartości do tablicy.

string[] array2 = new string[4] { "Sharing", "is", "Caring", "Right!" };
int max = Tools.LongestStringLenght(array2);//7

Jak widzisz ten przykład unika przeciążania metod ale za każdym razem musisz pisać dodatkowy kod przed metodą uzupełniający tablice argumentów.

Używając słowa kluczowego params możesz częściowo uniknąć tego problemu.

Deklarowanie tablicy params

Używasz słowa kluczowego params jako modyfikatora do tablicy parametrów. Na przykład oto ponownie metoda “LongestStringLenght” tym razem używa ona słowa kluczowego params.

class Tools
{
    public static int LongestStringLenght(params string[] paramList)

Jaki efekt ma to słowo kluczowe “params”. Pozwala to  metodzie na jej wywołanie  poprzez podanie dowolnej ilości argumentów string. Przykładowo użycie teraz tej metody dla 3 wartości wygląda tak.

int max2 = Tools.LongestStringLenght("Zdzisław", "Tomek", "Kaśka");

Kompilator tłumaczy ten kod w ten sposób.

string[] array = new string[3];
array[0] = "Zdzisław";
array[1] = "Tomek";
array[2] = "Kaśka";
int max = Tools.LongestStringLenght(array);

Oznacza to ,że różnica w działaniu istnieje tylko po naszej stronie. Dla kompilatora użycie słowa params niczego nie zmienia w działaniu metody.
  
Oto kod służący do odnalezienia długości  najdłuższego  napisu z 4 parametrów. Kompilator tłumaczy ten kod w podobny sposób.

int max3 = Tools.LongestStringLenght("Sharing", "is", "Caring", "Right!");

Obie metody wywołują tę samą metodę, ale z różną liczbą parametrów. Nie ma tutaj określonej liczby parametrów. Kompilator tworzy tablicę bazując na liczbie argumentów ,a potem wywołuje tę metodę.

Ograniczenia

Istnieją jednak pewne ograniczania przy użyciu słowa kluczowego params:

Nie możesz użyć słowa kluczowego params w tablicach wielowymiarowych.params 01
Nie możesz przeciążyć metody w taki sposób ,że jedna wersja używa params ,a druga nie. Słowo kluczowe  nie stanowi części sygnatury metody. Oznacza to ,że kompilator zgłosi błąd powtórzenia sygnatury metody.

params 02
Nie możesz używać modyfikatorów OUT i REF, jeśli tablica ma już modyfikator params.params 03
Parametr z tablicą params musi być ostatnim parametrem. Oznacza to ,że może być tylko jedna tablica z tym słowem kluczowym na metodę. Poprawne użycie jest poniżej.

public static int Suma(params int[] paramList,bool a,string wi)

Kompilator odrzuci takie metody przeciążone.

public static int LongestStringLenght(int a,params string[] paramList)
public static int LongestStringLenght(params string[] paramList)

Jednak o dziwo kompilator nie ma problemów z tym kodem, który też jest błędny.

public static int LongestStringLenght(string a,params string[] paramList)
{
    return 1;
}

To przeciążanie metody nie jest właściwe i niszczy działanie  słowa kluczowego. Od tej pory tylko ta wersja będzie się wykonywać. Kompilator powinien zgłosić błąd w końcu nie powinien być pewien, której wersji metody ma użyć ,ale o dziwo tak nie jest. Kompilator zdecydował  używać zawsze tej wersji. No, chyba że podam bezpośrednio tablice do metody.

params zepsucie

Podsumowując jest to też błąd.

Przeciążanie metod ze słowem kluczowym params tworzy jeszcze jeden problem. Oto przykład:

public static int LongestStringLenght(string a,string b)
{
    return 1;
}

Kompilator po raz kolejny nie zgłosi błędu. W tym wypadku, jeśli metoda nie przyjmuje dokładnie dwóch argumentów string zostanie wykonana wersja z params .To zachowanie jest niespójne,bo co jeśli jak chcę wykonać metodę params z dwoma parametrami string ,a przecież właśnie tego nie mogę zrobić. Jest to kolejny błąd. Nie ma tutaj spójności zachowania.

Używanie params object[]

Modyfikator ten jest użyteczny, gdy tablica  przechowuje konkretne typy jak string czy int.

Chociaż co jeśli chcesz nie tylko mieć możliwość podania dużej ilości argumentów ,ale też ich typów.

Typ “object” jest korzeniem wszystkich klas i kompilator potrafi stworzyć kod, który potrafi przekształcić typy wartościowe, które nie są klasami do obiektów używając pakowania. 

Object[] w połączeniu z params pozwala na utworzenie metody, która będzie akceptować jakąkolwiek liczbę argumentów o dowolnym typie. Teraz czas na przykład:

public static string MassToString(params object[] paramList)
{ 
    string[] tab = new string[paramList.Length];

    for (int i = 0; i < tab.Length; i++)
    {
        tab[i] = paramList[i].ToString();
    }
    return string.Join(",", tab);
}

Ta metoda pobiera obiekty i przetwarza wszystkie wyniki “ToString()” tych obiektów do postaci jednego łańcucha, który wyświetla te wyniki po przecinku.

Czy ta metoda ma jakieś wady? Czy o czymś zapomniałem?

Możesz użyć tej metody nie podając żadnych argumentów. Kompilator automatycznie stworzy tablicę z długością zero. Warto o tym pamiętać. Mój kod nie zwróci wyjątku w tym wypadku.

Tools.MassToString(null);

Możesz do tej metody wybrać parametr null, ponieważ tablica jest typem referencyjnym. W tym wypadku kod zwróci wyjątek. Pisząc tą metodę za pierwszym razem o tym nie pomyślałem.

Możesz do tej metody dodać faktyczną tablice obiektów.

object[] obje = new object[3];
obje[0] = true;
obje[1] = 11;
obje[2] = new StringInfo();
string napi = Tools.MassToString(obje);
// "True,11,System.Globalization.StringInfo"

Możesz też dodawać jakąkolwiek liczbę parametrów różnych typów do tej metody. Te argumenty automatycznie będą włożone do tablicy obiektów.

string napi2 = 
Tools.MassToString(1.10f,true,0.1,2,"Ala",StringSplitOptions.RemoveEmptyEntries);

Na tym kończę wpis na temat słowa kluczowego params. Życzę miłego dnia.