Навіщо використовувати ключове слово params?


336

Я знаю, що це основне питання, але я не зміг знайти відповідь.

Навіщо його використовувати? якщо ви пишете функцію або метод, який її використовує, при видаленні її код все одно буде працювати ідеально, на 100% як без нього. Наприклад:

З парамами:

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

Без парам:

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

62
Код самого методу все одно буде працювати чудово ... коду виклику цілком може не бути ...
Джон Скіт

7
params ключове слово означає ОПЦІЯЛЬНІ параметри, які можна передавати чи ні Методу. Масив з ключовим словом out params означає, що ВАМ потрібно передати аргумент масиву методу.
Айлайна Ентарія

Мова Python реалізує ту саму концепцію настільки солодко з *префіксом зірочки ( ), як зазначено тут .
RBT

Відповіді:


485

За допомогоюparams цього методу ви можете зателефонувати так:

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

Без цього paramsне можна.

Крім того, ви можете викликати метод з масивом як параметр в обох випадках :

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

Тобто paramsдозволяє використовувати ярлик при виклику методу.

Незалежно, ви можете різко скоротити свій метод:

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

17
@Ken: Можливо, вам потрібно буде імпортувати System.Linqпростір імен :)
Ranhiru Jude Cooray

49
Або повернути args.Sum (i => i + 2);
bornfromanegg

2
Сума з делегатом, однак, збільшує складність складеного коду, який потенційно може бути менш ефективним. Не дуже актуально в цій конкретній ситуації, оскільки це не призведе до закриття, але варто знати, що насправді робить компілятор, щоб зробити найкращий вибір.
Аллен Кларк Коупленд-молодший

2
Ви також можете скористатися return args.Select(x => x + 2).Sum();
bbvg

1
Коли ви додаєте, paramsви блокуєте себе від додавання додаткових аргументів методу, не порушуючи абонентів або роздільну здатність методу.
Містер Янг

93

Використання paramsдозволяє викликати функцію без аргументів. Без params:

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

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

    return sum;
}

addtwoEach(); // throws an error

Порівняти з params:

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

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

    return sum;
}

addtwoEach(); // returns 0

Як правило, ви можете використовувати парами, коли кількість аргументів може змінюватися від 0 до нескінченності, і використовувати масив, коли кількість аргументів змінюється від 1 до нескінченності.


13
насправді масив може бути порожнім. new int[0]. сподіваюся, що це допомагає! :)
vidstige

22

Це дозволяє додавати стільки параметрів базового типу у свій дзвінок, скільки вам подобається.

addTwoEach(10, 2, 4, 6)

тоді як для другої форми вам потрібно використовувати масив як параметр

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

17

Одна з небезпек для paramsключового слова полягає в тому, якщо після викликів методу були зашифровані,

  1. хтось випадково / навмисно видаляє один / більше необхідних параметрів із підпису методу та
  2. один / більше необхідних параметрів безпосередньо перед paramsПараметром до зміни підпису були типово сумісними з paramsпараметром,

ці Виклики будуть продовжувати компілювати з одним / декількома виразами, раніше призначеними для необхідних параметрів, які розглядаються як необов'язковий paramsпараметр. Я просто зіткнувся з найгіршим можливим випадком цього: paramsПараметр був типу object[].

Це примітно, оскільки розробники звикли компілятору плескати зап'ястя за набагато більш і більш поширеним сценарієм, коли Параметри вилучаються із методу з усіма необхідними параметрами (оскільки кількість очікуваних параметрів зміниться).

Для мене ярлик не вартий. (Type)[]без paramsбуде працювати з 0 до нескінченності # параметрів, не потребуючи переопределення. Найгірший випадок - вам доведеться додати , new (Type) [] {}дзвінки, коли це не застосовується.

Btw, imho, найбезпечнішою (і найбільш читаною практикою) є:

  1. пройти через іменовані параметри (що ми можемо зробити навіть через C # ~ 2 десятиліття після того, як ми могли б в VB; P) (тому що:

    1.1. це єдиний спосіб, який гарантує запобігання ненавмисним значенням, переданим Параметрам після порядку параметри, типу сумісного та / або підрахунку рахунку після кодування викликів,

    1.2. це зменшує ці шанси після зміни значення параметра Параметр, тому що ймовірне нове ім'я ідентифікатора, що відображає нове значення, знаходиться поруч із значенням, яке передається йому,

    1.3. це дозволяє уникнути підрахунку коми і переходити назад і назад з виклику до підпису, щоб побачити, який вираз передається за який параметр, і

    1.3.1. До речі, саме цієї причини повинно бути багато (з точки зору уникнення частих порушень, сприйнятливих до помилок Принципу DRY, лише щоб прочитати код, не кажучи також про його модифікацію ), але ця причина може бути експоненціально важливішою, якщо є одна / більше виразів, що передаються, містять коми, тобто багатовимірні відбиття масиву або виклики багатопараметричних функцій. У такому випадку ви навіть не можете використовувати (що, навіть якби ви могли, все-таки додаватимете додатковий крок за параметром за виклик методу), а знайдіть у вашому редакторі функцію "Виділення" у "Вибір усіх" для автоматизації підрахунку комів для вас.

    1.4. якщо вам потрібно скористатися додатковими параметрами ( paramsчи ні), це дозволяє шукати виклики, де проходить певний необов'язковий параметр (і, отже, швидше за все, це не має або принаймні має можливість не бути значенням за замовчуванням),

(ПРИМІТКА. Причини 1.2. Та 1.3. Можуть полегшити та зменшити шанси помилок навіть при кодуванні початкових викликів, не згадуючи, коли виклики повинні бути прочитані та / або змінені.))

і

  1. зробіть це ONE - PARAMETER - PER - LINE для кращої читабельності (тому що:

    2.1. він менш захаращений, і

    2.2. це дозволяє уникнути необхідності прокручування праворуч і назад ліворуч (і робити це PER - LINE, оскільки більшість смертних не можуть прочитати ліву частину кількох рядків, прокрутити праворуч і прочитати праву частину)).

    2.3. це відповідає "Кращій практиці", до якої ми вже переробили заяви про призначення, оскільки кожен пройдений параметр є по суті заявою про призначення (присвоєння значення або посилання на локальну змінну). Як і ті, хто дотримується останньої "найкращої практики" у стилі кодування, не мріяв би кодувати кілька заяв про призначення за рядком, ми, мабуть, не повинні (і колись "Краща практика" не може наздогнати мого "генія"; P ) зробіть це під час передачі параметрів.

ПРИМІТКИ :

  1. Передача змінних, назви яких відображають параметри, не допомагає, коли:

    1.1. ви переходите в буквальні константи (тобто прості 0/1, хибні / правдиві або нульові, що навіть "Найкращі практики" можуть не вимагати використання іменного константи для їх призначення, і це не може бути легко виведено з назви методу ),

    1.2. Метод є значно нижчим рівнем / більш загальним, ніж Викликаючий, так що ви не хочете / зможете б називати Ваші Змінні тими ж / схожими на Параметри (або навпаки), або

    1.3. ви будете повторно замовлення / заміна параметрів в підпису , що може привести в попередніх викликах ще компіляції , тому що типи трапляються по - , як і раніше бути сумісні.

  2. Наявність функції автоматичного обгортання, як VS, усуває лише ОДНУ (№2.2) з 8 причин, які я наводив вище. До того часу, як VS 2015 року, він не автоматично відступав (!? Дійсно, MS?!?), Що збільшує ступінь вираженості причини №2.1.

У VS повинна бути опція, яка генерує фрагменти методу виклику з іменованими параметрами (по одному на рядок, звичайно; P), і варіант компілятора, який вимагає іменованих параметрів (аналогічно поняттю до варіанту, явного в VB, який, btw, вимога був пролічений колись продуманий так само, як і обурливе, але зараз це вимагається "найкращими практиками"). Справді, «назад у моїхдень ";), у 1991 році, лише за кілька місяців моєї кар'єри, ще до того, як я використовував (або навіть бачив) мову з названими параметрами, у мене був анти-шеепл /" просто тому, що ти можеш, не означає, що ти повинен " / не сліпо "обрізати кінці смаженої" сенсу достатньо, щоб імітувати його (використовуючи вбудовані коментарі), не бачивши, щоб хтось це робив. Не потрібно використовувати іменовані параметри (як і інші синтаксиси, які зберігають "дорогоцінне" натискання клавіш вихідного коду) - це пережиток епохи Punch Card, коли почалася більшість цих синтаксисів. Немає виправдання для цього з сучасним обладнанням та IDE та набагато складнішим програмним забезпеченням, де читабельність значно, багато, МНОГОбільш важливим. "Код читається набагато частіше, ніж написано". Поки ви не дублюєте неавтоматизований код, кожен збережений набір клавіш, швидше за все, коштуватиме експоненціально дорожче, коли хтось (навіть ти сам) намагається прочитати його пізніше.


2
Я не розумію. Чому ви не можете просто примусити себе, щоб був принаймні один? Навіть без парам, ніщо не завадить тобі пройти nullабо new object[0]як аргумент.
Кейсі

Це, мабуть, занадто небезпечно, коли-небудь матимуть потрібні параметри перед необов'язковим (у випадку, якщо один або більше потрібних параметрів буде видалено після кодування викликів). Ось чому я ніколи не бачив потрібних параметрів перед необов’язковим параметром у зразковому коді в будь-яких документах із необов'язковими параметрами. Btw, imho, найбезпечніша (і найчитабельніша практика) - це пройти через іменовані параметри (що ми можемо зробити навіть за C # ~ 2 десятиліття після того, як ми могли в VB). У VS повинна бути опція, яка генерує фрагменти виклику методу з названими параметрами (і робити це 1 параметр на рядок).
Том

Я не дуже впевнений, що ти маєш на увазі. Єдиний спосіб, коли у вас можуть бути необхідні параметри та необов’язкові, - спочатку вказати всі необхідні параметри.
Кейсі

1
Вих. Я оголошую myMethodяк void myMethod(int requiredInt, params int[] optionalInts). I / хто - то інший код один / кілька викликів, тобто myMethod(1), myMethod(1, 21), myMethod(1, 21, 22). Я змінюсь myMethodбути void myMethod(params int[] optionalInts). Усі ці виклики все ще будуть компілюватися без помилок, навіть якщо їх 1-й параметр ("1") явно не призначений для передачі як 1-й елемент optionalIntsПараметра.
Том

Ой. Ну, гаразд, у цьому конкретному випадку це може бути недобросовісно. Я не думаю, що немає жодних причин уникати цього, якщо вам потрібен рядок і 0-до-багато вкладишів чи ще.
Кейсі

10

Не потрібно створювати методи перевантаження, просто використовуйте один метод з парами, як показано нижче

// 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);

Дякуємо за ваш внесок, але я не думаю, що подібні перевантаження взагалі не будуть рішенням, оскільки з paramsабо без нас ми просто передамо тип колекції для покриття будь-якої кількості колекцій.
MasterMastic

Ви в деякій мірі ви праві, але те, що робить його крутим - це перевантаження без вхідного параметра, наприклад, int sum1 = addTwoEach ();
електрика

5

params також дозволяє викликати метод одним аргументом.

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

тобто Foo(1);замість Foo(new int[] { 1 });. Може бути корисним для скорочення сценаріїв, коли вам може знадобитися передати одне значення, а не цілий масив. Це все ще обробляється аналогічно методу, але дає цукерки для виклику цього способу.


5

Додавання самого ключового слова params показує, що ви можете передавати декілька параметрів, викликаючи той метод, який неможливий без його використання. Щоб бути більш конкретним:

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

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

    return sum;
}

Коли ви зателефонуєте вище, ви можете викликати його будь-яким із наступних способів:

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

Але коли ви видалите ключове слово парами, третій спосіб із наведених способів буде добре працювати. У першому та другому випадку ви отримаєте помилку.


0

Необхідно виділити ще одну важливу річ. Краще використовувати, paramsтому що це краще для продуктивності. Коли ви викликаєте метод з paramsаргументом і передаєте йому нічого:

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

дзвінок:

ExampleMethod();

Тоді нові версії .Net Framework роблять це (з .Net Framework 4.6):

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

Цей Array.Emptyоб'єкт можна буде повторно використати за допомогою фреймворку пізніше, тому не потрібно робити зайві асигнування. Ці виділення відбудуться, коли ви називатимете цей метод так:

 ExampleMethod(new string[] {});

0

Можливо, це звучить дурно, але Params не дозволяє багатовимірний масив. Тоді як ви можете передати багатовимірний масив функції.


0

Ще один приклад

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

var items = Tokenize(product.Name, product.FullName, product.Xyz)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.