Зачем использовать ключевое слово 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 означает НЕОБЯЗАТЕЛЬНЫЕ параметры, которые могут передаваться или не передаваться в метод. Массив без ключевого слова params означает, что вы ДОЛЖНЫ передать аргумент массива методу.
Айлайна Энтарриа

Язык Python так же прекрасно реализует ту же концепцию с *префиксным параметром asterisk ( ), как упомянуто здесь .
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пространство имен :)
Ранхиру Джуд Курай,

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

Обычно вы можете использовать params, когда число аргументов может варьироваться от 0 до бесконечности, и использовать массив, когда число аргументов варьируется от 1 до бесконечности.


13
на самом деле массив может быть пустым. new int[0], надеюсь это поможет! :)
виджет

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) [] {}в Calls, где это не относится.

Кстати, имхо, самая безопасная (и наиболее читаемая практика) заключается в:

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

    1.1. это единственный способ, который гарантирует предотвращение непреднамеренных значений, передаваемых в параметры после изменения порядка параметров, типа совместимости и / или количества после кодирования вызовов;

    1.2. это уменьшает эти шансы после изменения значения параметра, поскольку вероятное имя нового идентификатора, отражающее новое значение, находится рядом с передаваемым ему значением,

    1.3. он избегает необходимости считать запятые и переходить назад и вперед из Call to Signature, чтобы увидеть, какое выражение передается для какого параметра, и

    1.3.1. Кстати, одной этой причины должно быть достаточно (с точки зрения избежания частых нарушений принципа DRY, подверженных ошибкам, только для того, чтобы прочитать код, не говоря уже о том, чтобы изменить его, а также изменить его), но эта причина может быть экспоненциально более важной, если есть одна / Передается больше выражений, которые сами содержат запятые, то есть ссылки на многомерный массив или вызовы функций с несколькими параметрами. В этом случае вы даже не могли бы использовать (что, даже если бы вы могли добавить дополнительный шаг для каждого параметра на вызов метода) функцию «Найти все вхождения» в функции выбора в вашем редакторе, чтобы автоматизировать подсчет запятых для вас.

    1.4. если вы должны использовать Необязательные параметры ( paramsили нет), это позволяет вам искать Звонки, где определенный Необязательный параметр пропущен (и, следовательно, скорее всего, нет или, по крайней мере, имеет возможность не быть значением по умолчанию),

(ПРИМЕЧАНИЕ. Причины 1.2. И 1.3. Могут облегчить и уменьшить вероятность ошибок даже при кодировании начальных вызовов, не говоря уже о том, когда вызовы должны быть прочитаны и / или изменены.))

и

  1. сделайте так ОДИН - ПАРАМЕТР - НА - ЛИНИЮ для лучшей читаемости (потому что:

    2.1. это менее загромождено, и

    2.2. он избегает необходимости прокручивать вправо и назад влево (и это необходимо делать PER - LINE, поскольку большинство смертных не может прочитать левую часть нескольких строк, прокрутить вправо и прочитать правую часть)).

    2,3. это согласуется с «передовой практикой», в которую мы уже вошли для операторов присваивания, потому что каждый переданный параметр по сути является оператором присваивания (присваивая значение или ссылку на локальную переменную). Точно так же, как те, кто следит за последней «Лучшей практикой» в стиле кодирования, не будут мечтать о кодировании нескольких операторов присваивания на строку, мы, вероятно, не должны этого делать (и однажды «Лучшая практика» не догонит моего «гения»; P) ) делать это при передаче параметров.

ЗАМЕЧАНИЯ :

  1. Передача переменных, чьи имена отражают параметры, не помогает, когда:

    1.1. вы передаете литеральные константы (т. е. простые 0/1, false / true или null, для которых даже в «Best Practices» может не потребоваться использование именованной константы, и их назначение не может быть легко определено из имени метода ),

    1.2. метод является значительно более низким уровнем / более общим, чем вызывающий, так что вы не захотите / не сможете назвать свои переменные такими же / похожими на параметры (или наоборот), или

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

  2. Наличие функции автоматического переноса типа VS исключает только ОДНУ (# 2.2) из ​​8 причин, которые я привел выше. До VS 2015 года это НЕ делало авто-отступ (!?! Действительно, MS?!?), Что увеличивает серьезность причины # 2.1.

В VS должна быть опция, которая генерирует фрагменты вызова метода с именованными параметрами (по одному на строку, конечно, P) и опцией компилятора, которая требует именованных параметров (аналогично концепции Option Explicit в VB, который, между прочим, требовался совсем недавно) в равной степени возмутительно, но в настоящее время требуется "" передовой опыт "). На самом деле "обратно в мойдень ";), в 1991 году, всего за несколько месяцев до моей карьеры, даже до того, как я использовал (или даже видел) язык с именованными параметрами, у меня был анти-овец /" просто потому что ты можешь, не значит, что должен " / не слепо «обрезать концы жаркого» достаточно, чтобы смоделировать его (используя встроенные комментарии), не видя, чтобы кто-то это делал. Не нужно использовать именованные параметры (а также другой синтаксис, сохраняющий «драгоценный») нажатия клавиш исходного кода) является пережитком эпохи перфокарт, когда началось большинство этих синтаксисов. Этому нет оправдания с современным оборудованием и средами IDE, а также с гораздо более сложным программным обеспечением, где читаемость намного, МНОГО , МНОГОболее важный. «Код читается гораздо чаще, чем пишется». Пока вы не дублируете не-автоматически обновляемый код, каждое сохраненное нажатие клавиш, вероятно, будет стоить экспоненциально дороже, если кто-то (даже вы) попытаетесь прочитать его позже.


2
Я не понимаю Почему вы не можете просто обеспечить, чтобы был хотя бы один? Даже без параметров нет ничего, что могло бы помешать вам передать nullили new object[0]в качестве аргумента.
Кейси

Вероятно, слишком опасно когда-либо иметь обязательные параметры до необязательного (в случае, если один или несколько из этих обязательных параметров удаляются после кодирования вызовов). Возможно, поэтому я никогда не видел обязательных параметров перед необязательным параметром в примере кода ни в одной документации по необязательным параметрам. Между прочим, imho, самая безопасная (и наиболее читаемая практика) - проходить через именованные параметры (что мы можем теперь делать даже в C # ~ 2 десятилетия после того, как мы могли в VB). В VS должна быть опция, которая генерирует фрагменты вызова метода с именованными параметрами (и делайте так по 1 параметру на строку).
Том

Я не совсем уверен, что вы имеете в виду. Единственный способ, которым вы можете иметь обязательные и дополнительные параметры, - это сначала указать все необходимые параметры.
Кейси

1
Ex. Я заявляю 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 ();
electricbah

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 })

Но когда вы удалите ключевое слово params, только третий способ из приведенных выше будет работать нормально. Для 1-го и 2-го случая вы получите ошибку.


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.