Dlaczego warto używać słowa kluczowego params?


336

Wiem, że to podstawowe pytanie, ale nie mogłem znaleźć odpowiedzi.

Po co z tego korzystać? jeśli napiszesz funkcję lub metodę, która jej używa, po jej usunięciu kod nadal będzie działał idealnie, w 100% jak bez niego. Na przykład:

Z params:

static public int addTwoEach(params int[] args)
{
    int sum = 0;
    foreach (var item in args)
        sum += item + 2;
    return sum;
}

Bez parametrów:

static public int addTwoEach(int[] args)
{
    int sum = 0;
    foreach (var item in args)
       sum += item + 2;
    return sum;
}

62
Kod samej metody nadal będzie działał idealnie ... kod wywołujący może nie działać ...
Jon Skeet

7
słowo kluczowe params oznacza OPCJONALNE parametry, które można przekazać lub nie do metody. Tablica bez słowa kluczowego params oznacza, że ​​MUSISZ przekazać argument metody do metody.
Ailayna Entarria

Język Python tak słodko implementuje tę samą koncepcję za pomocą *parametru z gwiazdką ( ), jak wspomniano tutaj .
RBT

Odpowiedzi:


485

Dziękiparams możesz wywołać swoją metodę w następujący sposób:

addTwoEach(1, 2, 3, 4, 5);

Bez tego paramsnie możesz.

Dodatkowo możesz wywołać metodę z tablicą jako parametrem w obu przypadkach :

addTwoEach(new int[] { 1, 2, 3, 4, 5 });

Oznacza to, paramsże można użyć skrótu podczas wywoływania metody.

Niepowiązane, możesz drastycznie skrócić swoją metodę:

public static int addTwoEach(params int[] args)
{
    return args.Sum() + 2 * args.Length;
}

17
@Ken: Być może trzeba zaimportować System.Linqprzestrzeń nazw :)
Ranhiru Jude Cooray

49
Lub zwróć args.Sum (i => i + 2);
bornfromanegg

2
Suma z delegatem zwiększa jednak złożoność skompilowanego kodu, co może potencjalnie być mniej wydajne. Nie bardzo istotne w tej konkretnej sytuacji, ponieważ nie spowodowałoby to żadnych zamknięć, ale warto wiedzieć, co faktycznie robi kompilator, aby dokonać najlepszego wyboru.
Allen Clark Copeland Jr

2
Możesz także użyć return args.Select(x => x + 2).Sum();
bbvg

1
Kiedy dodajesz params, blokujesz się przed dodawaniem dodatkowych argumentów metody bez przerywania wywołań lub rozwiązywania metody
Mr. Young,

93

Użycie paramsumożliwia wywołanie funkcji bez argumentów. Bez params:

static public int addTwoEach(int[] args)
{
    int sum = 0;

    foreach (var item in args)
    {
        sum += item + 2;
    }

    return sum;
}

addtwoEach(); // throws an error

Porównaj z params:

static public int addTwoEach(params int[] args)
{
    int sum = 0;

    foreach (var item in args)
    {
        sum += item + 2;
    }

    return sum;
}

addtwoEach(); // returns 0

Zasadniczo można używać parametrów, gdy liczba argumentów może wynosić od 0 do nieskończoności, i użyć tablicy, gdy liczba argumentów waha się od 1 do nieskończoności.


13
właściwie tablica może być pusta. new int[0]. mam nadzieję że to pomoże! :)
vidstige

22

Pozwala dodać dowolną liczbę parametrów typu podstawowego do połączenia.

addTwoEach(10, 2, 4, 6)

podczas gdy w drugim formularzu musisz użyć tablicy jako parametru

addTwoEach(new int[] {10,2,4,6})

17

Jednym ze zagrożeń związanych ze paramssłowem kluczowym jest to, że po zakodowaniu wywołań metody

  1. ktoś przypadkowo / celowo usuwa jeden / więcej wymaganych parametrów z podpisu metody, oraz
  2. co najmniej jeden wymagany parametr bezpośrednio przed paramsparametrem przed zmianą podpisu był zgodny z typem paramsparametru,

te wywołania będą się nadal kompilować z jednym / więcej Wyrażeniami uprzednio przeznaczonymi dla wymaganych parametrów traktowanych jako paramsparametr opcjonalny . Właśnie natrafiłem na najgorszy możliwy przypadek: paramsparametr był typu object[].

Jest to godne uwagi, ponieważ programiści są przyzwyczajeni do kompilatora uderzającego w nadgarstki w znacznie, znacznie bardziej powszechnym scenariuszu, w którym parametry są usuwane z metody ze wszystkimi wymaganymi parametrami (ponieważ liczba oczekiwanych parametrów zmieni się).

Dla mnie to nie jest warte skrótu. (Type)[]bez paramsbędzie działał z 0 do nieskończoności # parametrów bez potrzeby zastępowania. Najgorsze jest to, że będziesz musiał dodać , new (Type) [] {}do Połączenia, gdzie to nie dotyczy.

Btw, imho, najbezpieczniejszą (i najbardziej czytelną praktyką) jest:

  1. przechodzą przez Nazwane Parametry (co możemy teraz zrobić nawet w C # ~ 2 dekady po tym, jak mogliśmy w VB; P) (ponieważ:

    1.1 jest to jedyny sposób, który gwarantuje zapobieganie niezamierzonym wartościom przekazywanym do parametrów po zmianie kolejności parametrów, zgodności typu i / lub zmianie liczby po zakodowaniu wywołań,

    1.2 to zmniejsza te szanse po zmianie parametru znaczenie, ponieważ prawdopodobnie nowa nazwa odzwierciedla identyfikator nowego znaczenia jest tuż obok wartości były przekazywane do niej,

    1.3 unika się konieczności liczenia przecinków i przeskakiwania w przód i w tył od wezwania do podpisu, aby zobaczyć, jakie wyrażenie jest przekazywane dla danego parametru, oraz

    1.3.1 Nawiasem mówiąc, sam z tego powodu powinny być dużo (jeśli chodzi o unikanie częstych naruszeń podatne na błędy suchej Principle tylko do odczytu kodu nie wspomnieć także modyfikować go), ale powodem może być wykładniczo ważniejsze, jeśli istnieje jeden / więcej przekazywanych wyrażeń, które same zawierają przecinki, tj. wielowymiarowe odwołania do macierzy lub wywołania funkcji o wielu parametrach. W takim przypadku nie można nawet użyć (a nawet gdybyś mógł dodać dodatkowy krok na parametr w parametrze wywołania metody) funkcji Znajdź wszystkie wystąpienia w elemencie Wybór w edytorze, aby zautomatyzować liczenie przecinków.

    1.4 jeśli musisz użyć parametrów opcjonalnych ( paramslub nie), pozwala to na wyszukiwanie połączeń, w których przekazywany jest dany parametr opcjonalny (a zatem najprawdopodobniej nie ma go, a przynajmniej może nie być wartością domyślną),

(UWAGA: Powody 1.2. I 1.3. Mogą zmniejszyć i zmniejszyć ryzyko błędu nawet przy kodowaniu początkowych wywołań, nie mówiąc już o tym, kiedy połączenia muszą zostać odczytane i / lub zmienione.)

i

  1. zrób to JEDEN - PARAMETR - PER - LINE dla lepszej czytelności (ponieważ:

    2.1 jest mniej zagracony i

    2.2 unika się konieczności przewijania w prawo i wstecz w lewo (i robienia tego PER - LINE, ponieważ większość śmiertelników nie może odczytać lewej części wielu linii, przewijać w prawo i czytać prawą część).

    2.3 jest to zgodne z „najlepszymi praktykami”, do których już opracowaliśmy dla instrukcji przypisania, ponieważ każdy przekazywany parametr jest w istocie instrukcją przypisania (przypisywanie wartości lub odwołania do zmiennej lokalnej). Podobnie jak ci, którzy przestrzegają najnowszych „najlepszych praktyk” w stylu kodowania, nie marzyliby o kodowaniu wielu instrukcji przypisania w wierszu, prawdopodobnie nie powinniśmy (i nie raz, gdy „najlepsza praktyka” dogoni mojego „geniuszu”; P ) zrób to podczas przekazywania parametrów.

UWAGI :

  1. Przekazywanie zmiennych, których nazwy odzwierciedlają parametry, nie pomaga, gdy:

    1.1 podajesz Stałe Dosłowne (tj. proste 0/1, fałsz / prawda lub null, do którego nawet „Najlepsze praktyki” mogą nie wymagać użycia Nazwy Stałej, a ich celu nie można łatwo wywnioskować z nazwy Metody ),

    1.2 Metoda jest znacznie niższa / bardziej ogólna niż wywołująca, tak że nie chcesz / nie będziesz mógł nazwać swoich Zmiennych tak samo / podobnych do parametrów (lub odwrotnie), lub

    1.3 zamawiasz / zamieniasz parametry w podpisie, co może powodować kompilację wcześniejszych połączeń, ponieważ typy nadal kompatybilne.

  2. Posiadanie funkcji automatycznego zawijania, takiej jak VS, eliminuje tylko JEDEN (# 2.2) z 8 powodów, które podałem powyżej. Wcześniej, aż do VS 2015 r., NIE powodowało ono automatycznego wcięcia (!?! Naprawdę, MS?!?), Co zwiększa nasilenie przyczyny # 2.1.

VS powinien mieć opcję, która generuje fragmenty wywołania metody z nazwanymi parametrami (jeden na linię, oczywiście; P) oraz opcję kompilatora, która wymaga nazwanych parametrów (podobnie w koncepcji do opcji jawnej w VB, która, przy okazji, wymagała tego od razu równie oburzające, ale teraz jest „ wymagane ” przez „Najlepsze praktyki”). W rzeczywistości „z powrotem w moimdzień ";), w 1991 roku, zaledwie kilka miesięcy w mojej karierze, nawet zanim użyłem (lub nawet widziałem) język z Nazwanymi Parametrami, miałem anty-owczarek /" po prostu możesz, nie znaczy, że powinieneś " / nie ślepo „odcinaj końcówki pieczeni” wystarczająco, by go zasymulować (używając komentarzy w linii), nie widząc, że ktoś to robi. Nie musisz używać Nazwanych Parametrów (a także innej składni, która oszczędza „cenny” ” Kod źródłowy naciśnięcia klawiszy) jest reliktem epoki dziurkacza karty, gdy większość z tych składni zaczęło. nie ma usprawiedliwienia dla że z nowoczesnego sprzętu i IDE i znacznie bardziej złożonego oprogramowania gdzie czytelność jest dużo, dużo, dUŻOważniejsze. „Kod jest odczytywany znacznie częściej niż jest zapisywany”. Dopóki nie powielasz kodu nieaktualizowanego automatycznie, każde zapisane naciśnięcie klawisza może kosztować wykładniczo więcej, gdy ktoś (nawet ty) spróbuje go później przeczytać.


2
Nie rozumiem. Dlaczego nie możesz po prostu wymusić, że istnieje co najmniej jeden? Nawet bez parametrów nie ma nic, co powstrzymałoby cię przed przejściem nulllub new object[0]argumentem.
Casey

Prawdopodobnie jest to zbyt niebezpieczne, aby mieć wymagane parametry przed opcjonalnym (w przypadku, gdy jeden lub więcej wymaganych parametrów zostanie usuniętych po zakodowaniu połączeń). Być może dlatego nigdy nie widziałem wymaganych parametrów przed opcjonalnym parametrem w przykładowym kodzie w dokumentach dotyczących opcjonalnych parametrów. Btw, imho, najbezpieczniejszą (i najbardziej czytelną praktyką) jest przejście przez nazwane parametry (co możemy teraz zrobić nawet w C # ~ 2 dekady po tym, jak mogliśmy w VB). VS powinien mieć opcję, która generuje fragmenty wywołań metod z nazwanymi parametrami (i robi to 1 parametr na linię).
Tom

Nie jestem do końca pewien, co masz na myśli. Jedynym sposobem, w jaki możesz mieć wymagane parametry i opcjonalne, jest określenie wszystkich wymaganych w pierwszej kolejności.
Casey

1
Dawny. Deklaruję myMethodjako void myMethod(int requiredInt, params int[] optionalInts). Ja / ktoś kody też jeden / więcej połączeń, czyli myMethod(1), myMethod(1, 21), myMethod(1, 21, 22). Zmieniam się myMethodna void myMethod(params int[] optionalInts). Wszystkie te wywołania będą się nadal kompilować bez błędów, mimo że ich 1. parametry („1”) najwyraźniej nie były przeznaczone do przekazania jako pierwszy element optionalIntsparametru.
Tom

O. Cóż, OK, w tym konkretnym przypadku może to być niewłaściwa porada. Nie sądzę, aby istniał jakiś powód, aby tego unikać, jeśli potrzebujesz ciągu znaków i 0 do wielu liczb wewnętrznych itp.
Casey,

10

Nie trzeba tworzyć metod przeciążania, wystarczy użyć jednej metody z parametrami, jak pokazano poniżej

// Call params method with one to four integer constant parameters.
//
int sum0 = addTwoEach();
int sum1 = addTwoEach(1);
int sum2 = addTwoEach(1, 2);
int sum3 = addTwoEach(3, 3, 3);
int sum4 = addTwoEach(2, 2, 2, 2);

Dziękuję za Twój wkład, ale nie sądzę, aby tego rodzaju przeciążenia byłyby w ogóle rozwiązaniem, ponieważ z paramslub bez po prostu przekazalibyśmy typ kolekcji obejmujący dowolną liczbę kolekcji.
MasterMastic

W pewnym stopniu masz rację, ale to, co sprawia, że ​​jest fajne, to przeciążenie bez parametru wejściowego, np. Int sum1 = addTwoEach ();
electricbah

5

params pozwala również wywołać metodę za pomocą jednego argumentu.

private static int Foo(params int[] args) {
    int retVal = 0;
    Array.ForEach(args, (i) => retVal += i);
    return retVal;
}

tj. Foo(1);zamiast Foo(new int[] { 1 });. Może być przydatny do tworzenia skrótów w scenariuszach, w których może być konieczne podanie jednej wartości zamiast całej tablicy. Nadal jest obsługiwany w ten sam sposób, ale daje trochę cukierków do wywołania w ten sposób.


5

Samo dodanie słowa kluczowego params pokazuje, że można przekazać wiele parametrów podczas wywoływania tej metody, co nie jest możliwe bez jej użycia. Być bardziej specyficznym:

static public int addTwoEach(params int[] args)
{
    int sum = 0;

    foreach (var item in args)
    {
        sum += item + 2;
    }

    return sum;
}

Kiedy wywołasz powyższą metodę, możesz ją wywołać dowolnym z następujących sposobów:

  1. addTwoEach()
  2. addTwoEach(1)
  3. addTwoEach(new int[]{ 1, 2, 3, 4 })

Ale kiedy usuniesz słowo kluczowe params, tylko trzeci sposób z powyższych sposobów będzie działał dobrze. W przypadku 1. i 2. przypadku pojawi się błąd.


0

Należy podkreślić jeszcze jedną ważną rzecz. Lepiej jest używać, paramsponieważ jest lepszy pod względem wydajności. Kiedy wywołujesz metodę z paramsargumentem i przekazujesz do niej nic:

public void ExampleMethod(params string[] args)
{
// do some stuff
}

połączenie:

ExampleMethod();

Następnie robią to nowe wersje .Net Framework (z .Net Framework 4.6):

ExampleMethod(Array.Empty<string>());

Array.EmptyObiekt ten może być ponownie wykorzystany przez framework później, więc nie ma potrzeby wykonywania redundantnych alokacji. Te alokacje wystąpią, gdy wywołasz tę metodę w następujący sposób:

 ExampleMethod(new string[] {});

0

Może to brzmieć głupio, ale Params nie pozwala na wielowymiarową tablicę. Natomiast do funkcji można przekazać tablicę wielowymiarową.


0

Inny przykład

public IEnumerable<string> Tokenize(params string[] words)
{
  ...
}

var items = Tokenize(product.Name, product.FullName, product.Xyz)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.